数字と小数点を受け入れようとしていますが、符号はありません。
Windowsフォーム用のNumericUpDownコントロールを使用したサンプルと、 MicrosoftのNumericUpDownカスタムコントロールのサンプル を調べました。しかし、これまでのところ、NumericUpDown(WPFでサポートされているかどうかにかかわらず)が必要な機能を提供することはないようです。私のアプリケーションがどのように設計されているかについては、正しい考えを持っている人は誰も矢印をめちゃくちゃにしたくないでしょう。私のアプリケーションの文脈では、それらは実用的な意味を持ちません。
だから私は標準のWPF TextBoxが欲しい文字だけを受け入れるようにする簡単な方法を探しています。これは可能ですか?実用的ですか?
プレビューテキスト入力イベントを追加します。そのように:<TextBox PreviewTextInput="PreviewTextInput" />
。
テキストが許可されていない場合は、その中にe.Handled
を設定します。 e.Handled = !IsTextAllowed(e.Text);
IsTextAllowed
メソッドで単純な正規表現を使用して、それらが入力したものを許可する必要があるかどうかを確認します。私の場合は、数字、ドット、ダッシュだけを許可したいです。
private static readonly Regex _regex = new Regex("[^0-9.-]+"); //regex that matches disallowed text
private static bool IsTextAllowed(string text)
{
return !_regex.IsMatch(text);
}
間違ったデータの貼り付けを防ぐには、DataObject.Pasting
イベントDataObject.Pasting="TextBoxPasting"
を次のように接続します(コードの抜粋):
// Use the DataObject.Pasting Handler
private void TextBoxPasting(object sender, DataObjectPastingEventArgs e)
{
if (e.DataObject.GetDataPresent(typeof(String)))
{
String text = (String)e.DataObject.GetData(typeof(String));
if (!IsTextAllowed(text))
{
e.CancelCommand();
}
}
else
{
e.CancelCommand();
}
}
イベントハンドラはテキスト入力をプレビューしています。ここで正規表現は数字ではない場合にのみテキスト入力と一致し、そしてそれは入力textboxに作られません。
文字だけが必要な場合は、正規表現を[^a-zA-Z]
に置き換えます。
<TextBox Name="NumberTextBox" PreviewTextInput="NumberValidationTextBox"/>
using System.Text.RegularExpressions;
private void NumberValidationTextBox(object sender, TextCompositionEventArgs e)
{
Regex regex = new Regex("[^0-9]+");
e.Handled = regex.IsMatch(e.Text);
}
私はすでにここにあるもののいくつかを使い、それをビヘイビアを使ってひねっているので、このコードを大量のViewsに渡って伝播する必要はありません。
public class AllowableCharactersTextBoxBehavior : Behavior<TextBox>
{
public static readonly DependencyProperty RegularExpressionProperty =
DependencyProperty.Register("RegularExpression", typeof(string), typeof(AllowableCharactersTextBoxBehavior),
new FrameworkPropertyMetadata(".*"));
public string RegularExpression
{
get
{
return (string)base.GetValue(RegularExpressionProperty);
}
set
{
base.SetValue(RegularExpressionProperty, value);
}
}
public static readonly DependencyProperty MaxLengthProperty =
DependencyProperty.Register("MaxLength", typeof(int), typeof(AllowableCharactersTextBoxBehavior),
new FrameworkPropertyMetadata(int.MinValue));
public int MaxLength
{
get
{
return (int)base.GetValue(MaxLengthProperty);
}
set
{
base.SetValue(MaxLengthProperty, value);
}
}
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.PreviewTextInput += OnPreviewTextInput;
DataObject.AddPastingHandler(AssociatedObject, OnPaste);
}
private void OnPaste(object sender, DataObjectPastingEventArgs e)
{
if (e.DataObject.GetDataPresent(DataFormats.Text))
{
string text = Convert.ToString(e.DataObject.GetData(DataFormats.Text));
if (!IsValid(text, true))
{
e.CancelCommand();
}
}
else
{
e.CancelCommand();
}
}
void OnPreviewTextInput(object sender, System.Windows.Input.TextCompositionEventArgs e)
{
e.Handled = !IsValid(e.Text, false);
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.PreviewTextInput -= OnPreviewTextInput;
DataObject.RemovePastingHandler(AssociatedObject, OnPaste);
}
private bool IsValid(string newText, bool paste)
{
return !ExceedsMaxLength(newText, paste) && Regex.IsMatch(newText, RegularExpression);
}
private bool ExceedsMaxLength(string newText, bool paste)
{
if (MaxLength == 0) return false;
return LengthOfModifiedText(newText, paste) > MaxLength;
}
private int LengthOfModifiedText(string newText, bool paste)
{
var countOfSelectedChars = this.AssociatedObject.SelectedText.Length;
var caretIndex = this.AssociatedObject.CaretIndex;
string text = this.AssociatedObject.Text;
if (countOfSelectedChars > 0 || paste)
{
text = text.Remove(caretIndex, countOfSelectedChars);
return text.Length + newText.Length;
}
else
{
var insert = Keyboard.IsKeyToggled(Key.Insert);
return insert && caretIndex < text.Length ? text.Length : text.Length + newText.Length;
}
}
}
関連するビューコードは次のとおりです。
<TextBox MaxLength="50" TextWrapping="Wrap" MaxWidth="150" Margin="4"
Text="{Binding Path=FileNameToPublish}" >
<interactivity:Interaction.Behaviors>
<v:AllowableCharactersTextBoxBehavior RegularExpression="^[0-9.\-]+$" MaxLength="50" />
</interactivity:Interaction.Behaviors>
</TextBox>
これは WilP の答えを改良したものです。私の改善点は以下のとおりです。
EmptyValue
プロパティを追加/// <summary>
/// Regular expression for Textbox with properties:
/// <see cref="RegularExpression"/>,
/// <see cref="MaxLength"/>,
/// <see cref="EmptyValue"/>.
/// </summary>
public class TextBoxInputRegExBehaviour : Behavior<TextBox>
{
#region DependencyProperties
public static readonly DependencyProperty RegularExpressionProperty =
DependencyProperty.Register("RegularExpression", typeof(string), typeof(TextBoxInputRegExBehaviour), new FrameworkPropertyMetadata(".*"));
public string RegularExpression
{
get { return (string)GetValue(RegularExpressionProperty); }
set { SetValue(RegularExpressionProperty, value); }
}
public static readonly DependencyProperty MaxLengthProperty =
DependencyProperty.Register("MaxLength", typeof(int), typeof(TextBoxInputRegExBehaviour),
new FrameworkPropertyMetadata(int.MinValue));
public int MaxLength
{
get { return (int)GetValue(MaxLengthProperty); }
set { SetValue(MaxLengthProperty, value); }
}
public static readonly DependencyProperty EmptyValueProperty =
DependencyProperty.Register("EmptyValue", typeof(string), typeof(TextBoxInputRegExBehaviour), null);
public string EmptyValue
{
get { return (string)GetValue(EmptyValueProperty); }
set { SetValue(EmptyValueProperty, value); }
}
#endregion
/// <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);
}
#region Event handlers [PRIVATE] --------------------------------------
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();
}
#endregion Event handlers [PRIVATE] -----------------------------------
#region Auxiliary methods [PRIVATE] -----------------------------------
/// <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)
{
return (new Regex(this.RegularExpression, RegexOptions.IgnoreCase)).IsMatch(text) && (MaxLength == int.MinValue || text.Length <= MaxLength);
}
/// <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;
}
#endregion Auxiliary methods [PRIVATE] --------------------------------
}
使い方はとても簡単です。
<i:Interaction.Behaviors>
<behaviours:TextBoxInputRegExBehaviour RegularExpression="^\d+$" MaxLength="9" EmptyValue="0" />
</i:Interaction.Behaviors>
これは、MVVMを使用してこれを行うための非常に単純で簡単な方法です。
あなたのtextBoxをビューモデル内のintegerプロパティにバインドすれば、これはgemのように動作します。それは、非整数がtextboxに入力されたときに検証さえ表示するでしょう。
XAMLコード:
<TextBox x:Name="contactNoTxtBox" Text="{Binding contactNo}" />
モデルコードを見る:
private long _contactNo;
public long contactNo
{
get { return _contactNo; }
set
{
if (value == _contactNo)
return;
_contactNo = value;
OnPropertyChanged();
}
}
テキストが変更されたときにデータが数値であるかどうかを確認し、そうである場合は処理を続行できるようにし、そうでない場合は数値データのみがそのフィールドで受け入れられることをユーザーに確認します。
詳しくはWindows Presentation Foundationでの検証を参照してください。
単に検証規則を実装してTextBoxに適用することもできます。
<TextBox>
<TextBox.Text>
<Binding Path="OnyDigitInput" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<conv:OnlyDigitsValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
以下のような規則の実装で(他の答えで提案されたのと同じ正規表現を使用して):
public class OnlyDigitsValidationRule : ValidationRule
{
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
var validationResult = new ValidationResult(true, null);
if(value != null)
{
if (!string.IsNullOrEmpty(value.ToString()))
{
var regex = new Regex("[^0-9.-]+"); //regex that matches disallowed text
var parsingOk = !regex.IsMatch(value.ToString());
if (!parsingOk)
{
validationResult = new ValidationResult(false, "Illegal Characters, Please Enter Numeric Value");
}
}
}
return validationResult;
}
}
ここに私は Rayの答え に触発された簡単な解決策があります。これは、あらゆる形式の数字を識別するのに十分なはずです。
正数、整数値、または小数点以下の最大桁数まで正確な値などが必要な場合は、この解決方法を簡単に変更することもできます。
Rayの答え で示唆されているように、最初にPreviewTextInput
イベントを追加する必要があります。
<TextBox PreviewTextInput="TextBox_OnPreviewTextInput"/>
次に、以下のコードを後ろに置きます。
private void TextBox_OnPreviewTextInput(object sender, TextCompositionEventArgs e)
{
var textBox = sender as TextBox;
// Use SelectionStart property to find the caret position.
// Insert the previewed text into the existing text in the textbox.
var fullText = textBox.Text.Insert(textBox.SelectionStart, e.Text);
double val;
// If parsing is successful, set Handled to false
e.Handled = !double.TryParse(fullText, out val);
}
テンキーとバックスペースを許可しました。
private void TextBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
int key = (int)e.Key;
e.Handled = !(key >= 34 && key <= 43 ||
key >= 74 && key <= 83 ||
key == 2);
}
私はそれを仮定します:
数値入力のみを許可するTextBoxのTextプロパティには、最初は有効な数値(たとえば2.7172)が設定されています。
あなたのテキストボックスはあなたのメインウィンドウの子です
あなたのメインウィンドウはWindow1クラスです。
あなたのTextBoxの名前はnumericTBです。
基本的な考え方:
メインウィンドウクラス(Window1)にprivate string previousText;
を追加してください。
メインウィンドウのコンストラクタにpreviousText = numericTB.Text;
を追加します。
NumericTB.TextChangedイベントのハンドラを作成して、次のようにします。
private void numericTB_TextChanged(object sender, TextChangedEventArgs e)
{
double num = 0;
bool success = double.TryParse(((TextBox)sender).Text, out num);
if (success & num >= 0)
previousText = ((TextBox)sender).Text;
else
((TextBox)sender).Text = previousText;
}
これは、有効である限りpreviousTextをnumericTB.Textに設定し続け、ユーザーが気に入らないものを書いた場合はnumericTB.Textをその最後の有効な値に設定します。もちろん、これは単なる基本的な考えであり、「ばかな証拠」ではなく、単に「ばかに対する抵抗」です。たとえば、ユーザーがスペースと混同するケースは扱いません。だからここに私は "ばか証明"だと思う完全な解決策は、そして私が間違っているなら私に教えてください:
Window1.xamlファイルの内容
<Window x:Class="IdiotProofNumericTextBox.Window1"
xmlns="http://schemas.Microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.Microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid>
<TextBox Height="30" Width="100" Name="numericTB" TextChanged="numericTB_TextChanged"/>
</Grid>
</Window>
Window.xaml.csファイルの内容:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace IdiotProofNumericTextBox
{
public partial class Window1 : Window
{
private string previousText;
public Window1()
{
InitializeComponent();
previousText = numericTB.Text;
}
private void numericTB_TextChanged(object sender, TextChangedEventArgs e)
{
if (string.IsNullOrEmpty(((TextBox)sender).Text))
previousText = "";
else
{
double num = 0;
bool success = double.TryParse(((TextBox)sender).Text, out num);
if (success & num >= 0)
{
((TextBox)sender).Text.Trim();
previousText = ((TextBox)sender).Text;
}
else
{
((TextBox)sender).Text = previousText;
((TextBox)sender).SelectionStart = ((TextBox)sender).Text.Length;
}
}
}
}
}
以上です。あなたが多くのTextBoxを持っているならば私はTextBoxから継承するCustomControlを作成することをお勧めします、それであなたは別々のファイルでpreviousTextとnumericTB_TextChangedをラップすることができます。
もしあなたが基本的な機能をするためにたくさんのコードを書きたくないのなら(私はなぜ人々が長いメソッドを作るのかわかりません)あなたはこれをすることができます:
名前空間を追加します。
using System.Text.RegularExpressions;
XAMLで、TextChangedプロパティを設定します。
<TextBox x:Name="txt1" TextChanged="txt1_TextChanged"/>
Txt1_TextChangedメソッドの下のWPFで、Regex.Replace
を追加します。
private void txt1_TextChanged(object sender, TextChangedEventArgs e)
{
txt1.Text = Regex.Replace(txt1.Text, "[^0-9]+", "");
}
これが必要な唯一のコードです。
void MyTextBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
e.Handled = new Regex("[^0-9]+").IsMatch(e.Text);
}
これは、テキストボックスに数字を入力するのを許可します。
小数点またはマイナス記号を許可するには、正規表現を[^0-9.-]+
に変更します。
e.Handled = (int)e.Key >= 43 || (int)e.Key <= 34;
textboxのプレビューキーダウンイベント。
PreviewTextInput += (s, e) =>
{
e.Handled = !e.Text.All(char.IsDigit);
};
テキストボックス変更イベントで検証ができます。次の実装は、数値と小数点1桁以外のキー入力を防ぎます。
private void textBoxNumeric_TextChanged(object sender, TextChangedEventArgs e)
{
TextBox textBox = sender as TextBox;
Int32 selectionStart = textBox.SelectionStart;
Int32 selectionLength = textBox.SelectionLength;
String newText = String.Empty;
int count = 0;
foreach (Char c in textBox.Text.ToCharArray())
{
if (Char.IsDigit(c) || Char.IsControl(c) || (c == '.' && count == 0))
{
newText += c;
if (c == '.')
count += 1;
}
}
textBox.Text = newText;
textBox.SelectionStart = selectionStart <= textBox.Text.Length ? selectionStart : textBox.Text.Length;
}
もう1つのアプローチは添付の振る舞いを使用することです。私は私のカスタム TextBoxHelper クラスを実装しました。これは私のプロジェクト全体のテキストボックスで使用できます。この目的のためにすべてのテキストボックスおよびすべての個々のXAMLファイルのイベントを購読するのは時間がかかる可能性があると私が考えたからです。
私が実装したTextBoxHelperクラスには、次の機能があります。
これがTextBoxHelperクラスの実装です。
public static class TextBoxHelper
{
#region Enum Declarations
public enum NumericFormat
{
Double,
Int,
Uint,
Natural
}
public enum EvenOddConstraint
{
All,
OnlyEven,
OnlyOdd
}
#endregion
#region Dependency Properties & CLR Wrappers
public static readonly DependencyProperty OnlyNumericProperty =
DependencyProperty.RegisterAttached("OnlyNumeric", typeof(NumericFormat?), typeof(TextBoxHelper),
new PropertyMetadata(null, DependencyPropertiesChanged));
public static void SetOnlyNumeric(TextBox element, NumericFormat value) =>
element.SetValue(OnlyNumericProperty, value);
public static NumericFormat GetOnlyNumeric(TextBox element) =>
(NumericFormat) element.GetValue(OnlyNumericProperty);
public static readonly DependencyProperty DefaultValueProperty =
DependencyProperty.RegisterAttached("DefaultValue", typeof(string), typeof(TextBoxHelper),
new PropertyMetadata(null, DependencyPropertiesChanged));
public static void SetDefaultValue(TextBox element, string value) =>
element.SetValue(DefaultValueProperty, value);
public static string GetDefaultValue(TextBox element) => (string) element.GetValue(DefaultValueProperty);
public static readonly DependencyProperty EvenOddConstraintProperty =
DependencyProperty.RegisterAttached("EvenOddConstraint", typeof(EvenOddConstraint), typeof(TextBoxHelper),
new PropertyMetadata(EvenOddConstraint.All, DependencyPropertiesChanged));
public static void SetEvenOddConstraint(TextBox element, EvenOddConstraint value) =>
element.SetValue(EvenOddConstraintProperty, value);
public static EvenOddConstraint GetEvenOddConstraint(TextBox element) =>
(EvenOddConstraint)element.GetValue(EvenOddConstraintProperty);
#endregion
#region Dependency Properties Methods
private static void DependencyPropertiesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (!(d is TextBox textBox))
throw new Exception("Attached property must be used with TextBox.");
switch (e.Property.Name)
{
case "OnlyNumeric":
{
var castedValue = (NumericFormat?) e.NewValue;
if (castedValue.HasValue)
{
textBox.PreviewTextInput += TextBox_PreviewTextInput;
DataObject.AddPastingHandler(textBox, TextBox_PasteEventHandler);
}
else
{
textBox.PreviewTextInput -= TextBox_PreviewTextInput;
DataObject.RemovePastingHandler(textBox, TextBox_PasteEventHandler);
}
break;
}
case "DefaultValue":
{
var castedValue = (string) e.NewValue;
if (castedValue != null)
{
textBox.TextChanged += TextBox_TextChanged;
}
else
{
textBox.TextChanged -= TextBox_TextChanged;
}
break;
}
}
}
#endregion
private static void TextBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
var textBox = (TextBox)sender;
string newText;
if (textBox.SelectionLength == 0)
{
newText = textBox.Text.Insert(textBox.SelectionStart, e.Text);
}
else
{
var textAfterDelete = textBox.Text.Remove(textBox.SelectionStart, textBox.SelectionLength);
newText = textAfterDelete.Insert(textBox.SelectionStart, e.Text);
}
var evenOddConstraint = GetEvenOddConstraint(textBox);
switch (GetOnlyNumeric(textBox))
{
case NumericFormat.Double:
{
if (double.TryParse(newText, out double number))
{
switch (evenOddConstraint)
{
case EvenOddConstraint.OnlyEven:
if (number % 2 != 0)
e.Handled = true;
else
e.Handled = false;
break;
case EvenOddConstraint.OnlyOdd:
if (number % 2 == 0)
e.Handled = true;
else
e.Handled = false;
break;
}
}
else
e.Handled = true;
break;
}
case NumericFormat.Int:
{
if (int.TryParse(newText, out int number))
{
switch (evenOddConstraint)
{
case EvenOddConstraint.OnlyEven:
if (number % 2 != 0)
e.Handled = true;
else
e.Handled = false;
break;
case EvenOddConstraint.OnlyOdd:
if (number % 2 == 0)
e.Handled = true;
else
e.Handled = false;
break;
}
}
else
e.Handled = true;
break;
}
case NumericFormat.Uint:
{
if (uint.TryParse(newText, out uint number))
{
switch (evenOddConstraint)
{
case EvenOddConstraint.OnlyEven:
if (number % 2 != 0)
e.Handled = true;
else
e.Handled = false;
break;
case EvenOddConstraint.OnlyOdd:
if (number % 2 == 0)
e.Handled = true;
else
e.Handled = false;
break;
}
}
else
e.Handled = true;
break;
}
case NumericFormat.Natural:
{
if (uint.TryParse(newText, out uint number))
{
if (number == 0)
e.Handled = true;
else
{
switch (evenOddConstraint)
{
case EvenOddConstraint.OnlyEven:
if (number % 2 != 0)
e.Handled = true;
else
e.Handled = false;
break;
case EvenOddConstraint.OnlyOdd:
if (number % 2 == 0)
e.Handled = true;
else
e.Handled = false;
break;
}
}
}
else
e.Handled = true;
break;
}
}
}
private static void TextBox_PasteEventHandler(object sender, DataObjectPastingEventArgs e)
{
var textBox = (TextBox)sender;
if (e.DataObject.GetDataPresent(typeof(string)))
{
var clipboardText = (string) e.DataObject.GetData(typeof(string));
var newText = textBox.Text.Insert(textBox.SelectionStart, clipboardText);
var evenOddConstraint = GetEvenOddConstraint(textBox);
switch (GetOnlyNumeric(textBox))
{
case NumericFormat.Double:
{
if (double.TryParse(newText, out double number))
{
switch (evenOddConstraint)
{
case EvenOddConstraint.OnlyEven:
if (number % 2 != 0)
e.CancelCommand();
break;
case EvenOddConstraint.OnlyOdd:
if (number % 2 == 0)
e.CancelCommand();
break;
}
}
else
e.CancelCommand();
break;
}
case NumericFormat.Int:
{
if (int.TryParse(newText, out int number))
{
switch (evenOddConstraint)
{
case EvenOddConstraint.OnlyEven:
if (number % 2 != 0)
e.CancelCommand();
break;
case EvenOddConstraint.OnlyOdd:
if (number % 2 == 0)
e.CancelCommand();
break;
}
}
else
e.CancelCommand();
break;
}
case NumericFormat.Uint:
{
if (uint.TryParse(newText, out uint number))
{
switch (evenOddConstraint)
{
case EvenOddConstraint.OnlyEven:
if (number % 2 != 0)
e.CancelCommand();
break;
case EvenOddConstraint.OnlyOdd:
if (number % 2 == 0)
e.CancelCommand();
break;
}
}
else
e.CancelCommand();
break;
}
case NumericFormat.Natural:
{
if (uint.TryParse(newText, out uint number))
{
if (number == 0)
e.CancelCommand();
else
{
switch (evenOddConstraint)
{
case EvenOddConstraint.OnlyEven:
if (number % 2 != 0)
e.CancelCommand();
break;
case EvenOddConstraint.OnlyOdd:
if (number % 2 == 0)
e.CancelCommand();
break;
}
}
}
else
{
e.CancelCommand();
}
break;
}
}
}
else
{
e.CancelCommand();
}
}
private static void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
var textBox = (TextBox)sender;
var defaultValue = GetDefaultValue(textBox);
var evenOddConstraint = GetEvenOddConstraint(textBox);
switch (GetOnlyNumeric(textBox))
{
case NumericFormat.Double:
{
if (double.TryParse(textBox.Text, out double number))
{
switch (evenOddConstraint)
{
case EvenOddConstraint.OnlyEven:
if (number % 2 != 0)
textBox.Text = defaultValue;
break;
case EvenOddConstraint.OnlyOdd:
if (number % 2 == 0)
textBox.Text = defaultValue;
break;
}
}
else
textBox.Text = defaultValue;
break;
}
case NumericFormat.Int:
{
if (int.TryParse(textBox.Text, out int number))
{
switch (evenOddConstraint)
{
case EvenOddConstraint.OnlyEven:
if (number % 2 != 0)
textBox.Text = defaultValue;
break;
case EvenOddConstraint.OnlyOdd:
if (number % 2 == 0)
textBox.Text = defaultValue;
break;
}
}
else
textBox.Text = defaultValue;
break;
}
case NumericFormat.Uint:
{
if (uint.TryParse(textBox.Text, out uint number))
{
switch (evenOddConstraint)
{
case EvenOddConstraint.OnlyEven:
if (number % 2 != 0)
textBox.Text = defaultValue;
break;
case EvenOddConstraint.OnlyOdd:
if (number % 2 == 0)
textBox.Text = defaultValue;
break;
}
}
else
textBox.Text = defaultValue;
break;
}
case NumericFormat.Natural:
{
if (uint.TryParse(textBox.Text, out uint number))
{
if(number == 0)
textBox.Text = defaultValue;
else
{
switch (evenOddConstraint)
{
case EvenOddConstraint.OnlyEven:
if (number % 2 != 0)
textBox.Text = defaultValue;
break;
case EvenOddConstraint.OnlyOdd:
if (number % 2 == 0)
textBox.Text = defaultValue;
break;
}
}
}
else
{
textBox.Text = defaultValue;
}
break;
}
}
}
}
そして、これが簡単な使い方の例です。
<TextBox viewHelpers:TextBoxHelper.OnlyNumeric="Double"
viewHelpers:TextBoxHelper.DefaultValue="1"/>
または
<TextBox viewHelpers:TextBoxHelper.OnlyNumeric="Natural"
viewHelpers:TextBoxHelper.DefaultValue="3"
viewHelpers:TextBoxHelper.EvenOddConstraint="OnlyOdd"/>
私のTextBoxHelperはviewHelpers xmlnsエイリアスにあることに注意してください。
この実装が他の人の作業を楽にすることを願っています:)
Windowsフォームでは簡単でした。あなたはKeyPressのためのイベントを追加することができ、すべてが簡単に動作します。しかし、WPFではそのイベントはありません。しかし、もっと簡単な方法があります。
WPFのTextBoxには、すべてに共通のTextChangedイベントがあります。それには貼り付け、タイピングそしてあなたの頭に浮かび上がるものは何でも含まれます。
だから、あなたはこのようなことをすることができます:
XAML:
<TextBox name="txtBox1" ... TextChanged="TextBox_TextChanged"/>
コードの後ろに:
private void TextBox_TextChanged(object sender, TextChangedEventArgs e) {
string s = Regex.Replace(((TextBox)sender).Text, @"[^\d.]", "");
((TextBox)sender).Text = s;
}
これは.
も受け付けます。不要な場合はregex
ステートメントから削除して@[^\d]
にしてください。
注:このイベントはsender
オブジェクトのTextを使用しているため、多くのTextBoxで使用できます。あなたは一度だけイベントを書き、それを複数のTextBoxに使うことができます。
整数と10進数のみを使用してこのタイプの問題を素早く簡単に実装するには、PreviewTextInput
プロパティをTextBox
に追加してから、xaml.csファイルで使用します。
private void Text_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
e.Handled = !char.IsDigit(e.Text.Last()) && !e.Text.Last() == '.';
}
科学的表記で何かをしているのでなければ、毎回文字列全体をチェックし続けるのはちょっと冗長です(たとえ、 'e'のような特定の文字を追加する場合は、記号/文字を追加する単純な正規表現は本当にシンプルで他の答えで説明されています)。しかし、単純な浮動小数点値の場合は、この解決方法で十分です。
ラムダ式でワンライナーとして書かれています:
private void Text_PreviewTextInput(object sender, TextCompositionEventArgs e) => e.Handled = !char.IsDigit(e.Text.Last() && !e.Text.Last() == '.');
今、私はこの質問が 受け入れられた答え を持っていることを知っています、しかし個人的に、私はそれを少し混乱させると思います、そしてそれはそれより簡単であるべきだと思います。それで、私はそれが可能な限りうまくいくように私がそれを得た方法を示すことを試みるつもりです:
Windows Formsには、この種のタスクに最適なKeyPress
name__というイベントがあります。ただし、これはWPFには存在しないため、代わりにPreviewTextInput
name__イベントを使用します。また、検証のために、foreach
name__を使用してtextbox.Text
をループ処理し、それが条件(matches;))であるかどうかをチェックできると思いますが、正直なところ、これが 正規表現 の目的です。
聖なるコードに入る前にもう1つ。イベントが発生するためには、2つのことができます。
<PreviewTextInput="textBox_PreviewTextInput/>
Loaded
name__イベントで行います。textBox.PreviewTextInput += onlyNumeric;
このような状況では、ほとんど同じ条件(regex
name__)を複数のTextBox
name__および 自分で繰り返す必要はありません に適用する必要があるため、2番目の方法の方が良いと思います。
最後に、やり方は次のとおりです。
private void onlyNumeric(object sender, TextCompositionEventArgs e)
{
string onlyNumeric = @"^([0-9]+(.[0-9]+)?)$";
Regex regex = new Regex(onlyNumeric);
e.Handled = !regex.IsMatch(e.Text);
}
数値を確認するときは、 VisualBasic.IsNumeric 関数を使用できます。
つかいます:
Private Sub DetailTextBox_PreviewTextInput( _
ByVal sender As Object, _
ByVal e As System.Windows.Input.TextCompositionEventArgs) _
Handles DetailTextBox.PreviewTextInput
If _IsANumber Then
If Not Char.IsNumber(e.Text) Then
e.Handled = True
End If
End If
End Sub
私は自分が取り組んでいた単純なプロジェクトのために箱のない箱を使って作業していたので、標準的な拘束方法を使用することはできませんでした。その結果、私は既存のTextBoxコントロールを単純に拡張することによって他の人が非常に便利だと思う簡単なハックを作成しました:
namespace MyApplication.InterfaceSupport
{
public class NumericTextBox : TextBox
{
public NumericTextBox() : base()
{
TextChanged += OnTextChanged;
}
public void OnTextChanged(object sender, TextChangedEventArgs changed)
{
if (!String.IsNullOrWhiteSpace(Text))
{
try
{
int value = Convert.ToInt32(Text);
}
catch (Exception e)
{
MessageBox.Show(String.Format("{0} only accepts numeric input.", Name));
Text = "";
}
}
}
public int? Value
{
set
{
if (value != null)
{
this.Text = value.ToString();
}
else
Text = "";
}
get
{
try
{
return Convert.ToInt32(this.Text);
}
catch (Exception ef)
{
// Not numeric.
}
return null;
}
}
}
}
明らかに、浮動小数点型の場合は、それを浮動小数点数などとして解析します。同じ原則が適用されます。
次にXAMLファイルに、関連する名前空間を含める必要があります。
<UserControl x:Class="MyApplication.UserControls.UnParameterisedControl"
[ Snip ]
xmlns:interfaceSupport="clr-namespace:MyApplication.InterfaceSupport"
>
その後は通常のコントロールとして使用できます。
<interfaceSupport:NumericTextBox Height="23" HorizontalAlignment="Left" Margin="168,51,0,0" x:Name="NumericBox" VerticalAlignment="Top" Width="120" >
ここでしばらく解決策をいくつか使用した後、私は自分のMVVMセットアップに適した独自のものを開発しました。ユーザーが誤った文字を入力できるという意味では、他の文字ほど動的ではありませんが、ボタンを押して何もすることをブロックします。これは、アクションを実行できないときにボタンをグレー表示にするという私のテーマに適しています。
私はTextBox
を持っていて、それはユーザーが印刷されるべき多くのドキュメントページを入力しなければならないということです:
<TextBox Text="{Binding NumberPagesToPrint, UpdateSourceTrigger=PropertyChanged}"/>
...このバインディングプロパティを使って:
private string _numberPagesToPrint;
public string NumberPagesToPrint
{
get { return _numberPagesToPrint; }
set
{
if (_numberPagesToPrint == value)
{
return;
}
_numberPagesToPrint = value;
OnPropertyChanged("NumberPagesToPrint");
}
}
ボタンもあります。
<Button Template="{DynamicResource CustomButton_Flat}" Content="Set"
Command="{Binding SetNumberPagesCommand}"/>
...このコマンドバインディングでは:
private RelayCommand _setNumberPagesCommand;
public ICommand SetNumberPagesCommand
{
get
{
if (_setNumberPagesCommand == null)
{
int num;
_setNumberPagesCommand = new RelayCommand(param => SetNumberOfPages(),
() => Int32.TryParse(NumberPagesToPrint, out num));
}
return _setNumberPagesCommand;
}
}
それからSetNumberOfPages()
のメソッドがありますが、それはこのトピックにとって重要ではありません。ビューの分離コードファイルにコードを追加する必要がなく、Command
プロパティを使用して動作を制御できるため、これは私の場合はうまく機能します。
WPFアプリケーションでは、TextChanged
イベントを処理することでこれを処理できます。
void arsDigitTextBox_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
{
Regex regex = new Regex("[^0-9]+");
bool handle = regex.IsMatch(this.Text);
if (handle)
{
StringBuilder dd = new StringBuilder();
int i = -1;
int cursor = -1;
foreach (char item in this.Text)
{
i++;
if (char.IsDigit(item))
dd.Append(item);
else if(cursor == -1)
cursor = i;
}
this.Text = dd.ToString();
if (i == -1)
this.SelectionStart = this.Text.Length;
else
this.SelectionStart = cursor;
}
}
これが私のバージョンです。これは基本的なValidatingTextBox
クラスに基づいています。これは「有効でない」場合に行われたことを元に戻すだけです。貼り付け、切り取り、削除、バックスペース、+、 - などをサポートします。
32ビット整数の場合、intと比較するだけのInt32TextBoxクラスがあります。浮動小数点検証クラスも追加しました。
public class ValidatingTextBox : TextBox
{
private bool _inEvents;
private string _textBefore;
private int _selectionStart;
private int _selectionLength;
public event EventHandler<ValidateTextEventArgs> ValidateText;
protected override void OnPreviewKeyDown(KeyEventArgs e)
{
if (_inEvents)
return;
_selectionStart = SelectionStart;
_selectionLength = SelectionLength;
_textBefore = Text;
}
protected override void OnTextChanged(TextChangedEventArgs e)
{
if (_inEvents)
return;
_inEvents = true;
var ev = new ValidateTextEventArgs(Text);
OnValidateText(this, ev);
if (ev.Cancel)
{
Text = _textBefore;
SelectionStart = _selectionStart;
SelectionLength = _selectionLength;
}
_inEvents = false;
}
protected virtual void OnValidateText(object sender, ValidateTextEventArgs e) => ValidateText?.Invoke(this, e);
}
public class ValidateTextEventArgs : CancelEventArgs
{
public ValidateTextEventArgs(string text) => Text = text;
public string Text { get; }
}
public class Int32TextBox : ValidatingTextBox
{
protected override void OnValidateText(object sender, ValidateTextEventArgs e) => e.Cancel = !int.TryParse(e.Text, out var value);
}
public class Int64TextBox : ValidatingTextBox
{
protected override void OnValidateText(object sender, ValidateTextEventArgs e) => e.Cancel = !long.TryParse(e.Text, out var value);
}
public class DoubleTextBox : ValidatingTextBox
{
protected override void OnValidateText(object sender, ValidateTextEventArgs e) => e.Cancel = !double.TryParse(e.Text, out var value);
}
public class SingleTextBox : ValidatingTextBox
{
protected override void OnValidateText(object sender, ValidateTextEventArgs e) => e.Cancel = !float.TryParse(e.Text, out var value);
}
public class DecimalTextBox : ValidatingTextBox
{
protected override void OnValidateText(object sender, ValidateTextEventArgs e) => e.Cancel = !decimal.TryParse(e.Text, out var value);
}
注意1:WPFバインディングを使用するときは、他の方法でバウンドプロパティタイプに適合するクラスを必ず使用する必要があります。そうしないと、奇妙な結果になることがあります。
注意2:WPFバインディングで浮動小数点クラスを使用するときは、バインディングが現在のカルチャを使用して、私が使用したTryParseメソッドと一致するようにしてください。
これはどう?私にとってはうまくいきます。 Edgeのケースを見逃していないことを願っています...
MyTextBox.PreviewTextInput += (sender, args) =>
{
if (!int.TryParse(args.Text, out _))
{
args.Handled = true;
}
};
DataObject.AddPastingHandler(MyTextBox, (sender, args) =>
{
var isUnicodeText = args.SourceDataObject.GetDataPresent(DataFormats.UnicodeText, true);
if (!isUnicodeText)
{
args.CancelCommand();
}
var data = args.SourceDataObject.GetData(DataFormats.UnicodeText) as string;
if (!int.TryParse(data, out _))
{
args.CancelCommand();
}
});
WPF
<TextBox PreviewTextInput="Port_PreviewTextInput" MaxLines="1"/>
C#
private void Port_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
e.Handled = !int.TryParse(e.Text, out int x);
}
これは、数字と小数点を受け入れるWPFテキストボックスを取得するために使用するものです。
class numericTextBox : TextBox
{
protected override void OnKeyDown(KeyEventArgs e)
{
bool b = false;
switch (e.Key)
{
case Key.Back: b = true; break;
case Key.D0: b = true; break;
case Key.D1: b = true; break;
case Key.D2: b = true; break;
case Key.D3: b = true; break;
case Key.D4: b = true; break;
case Key.D5: b = true; break;
case Key.D6: b = true; break;
case Key.D7: b = true; break;
case Key.D8: b = true; break;
case Key.D9: b = true; break;
case Key.OemPeriod: b = true; break;
}
if (b == false)
{
e.Handled = true;
}
base.OnKeyDown(e);
}
}
新しいクラスファイルにコードを追加し、追加します
using System.Windows.Controls;
using System.Windows.Input;
ファイルの先頭でソリューションをビルドします。その後、numericTextBoxコントロールがツールボックスの上部に表示されます。