我的值格式为[double-type-value] [unit],其中单位可以是“ g”或“ mg”(g表示克,mg表示毫克)。有没有一种方法允许用户仅以该格式在TextBox中输入文本。例如,像迷你文本框,仅接受数字,迷你组合框,其中普通文本框中的值为“ g”或“ mg”或其他?在文本框中输入某些内容之前,最好将单位的默认值设置为“ g”,这样,如果有更多的文本框,用户不必在文本框末尾键入g或mg。
编辑 我使用的是MVVM模式,因此后面的代码违反了它。
答案 0 :(得分:1)
您实际上应该处理三个事件:
PreviewTextInput
PreviewKeyDown
-防止输入空白字符,因为它们不是由PreviewTextInput处理的DataObject.Pasting
附加事件,以防止用户从剪贴板粘贴无效文本最好将此逻辑封装在行为中。 有类似行为的示例:TextBoxIntegerInputBehavior,TextBoxDoubleInputBehavior。
答案 1 :(得分:0)
您可以对PreviewTextInput
上的事件DataObject.Pasting
,PreviewKeyDown
和TextBox
使用正则表达式,以检查新字符串是否与regex
相匹配,如果没有,则可以取消操作。
像这样:
xaml
:
...
<TextBox PreviewTextInput="txtbox_PreviewTextInput" DataObject.Pasting="txtbox_Pasting" PreviewKeyDown="txtbox_PreviewKeyDown" />
...
后面的代码:
public partial class MainWindow : Window
{
private Regex gramOrMilliGramRegex = new Regex("^[0-9.-]+(m?g)?$");
public MainWindow ()
{
InitializeComponent();
}
private void txtbox_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
if(sender is TextBox txtbox)
{
string newString = txtbox.Text.Substring(0, txtbox.CaretIndex) + e.Text + txtbox.Text.Substring(txtbox.CaretIndex); //Build the new string
e.Handled = !gramOrMilliGramRegex.IsMatch(e.Text); //Check if it matches the regex
}
}
private void txtbox_Pasting(object sender, DataObjectPastingEventArgs e)
{
if(sender is TextBox txtbox)
{
string newString = txtbox.Text.Substring(0, txtbox.CaretIndex) + e.DataObject.GetData(typeof(string)) as string + txtbox.Text.Substring(txtbox.CaretIndex); //Build new string
if (!digitOnlyRegex.IsMatch(newString)) //Check if it matches the regex
{
e.CancelCommand();
}
}
private void txtbox_PreviewKeyDown(object sender, KeyEventArgs e)
{
//Prevents whitespace
if (e.Key == Key.Space)
{
e.Handled = true;
}
base.OnPreviewKeyDown(e);
}
}
更新:正如您现在所提到的,您正在使用MVVM,并且不想违反该模式。
您将需要将这些事件路由到ViewModel
中的命令,并将这些事件放在上面。
您可以通过在xaml的TextBox
中使用以下代码来做到这一点:
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:cmd ="http://www.galasoft.ch/mvvmlight"
...
<TextBox>
<i:Interaction.Triggers>
<i:EventTrigger EventName="PreviewTextInput">
<cmd:EventToCommand Command="{Binding Mode=OneWay, Path=PreviewTextInputCommand}" PassEventArgsToCommand="True" />
</i:EventTrigger>
<i:EventTrigger EventName="DataObject.Pasting">
<cmd:EventToCommand Command="{Binding Mode=OneWay, Path=DataObject_PastingCommand}" PassEventArgsToCommand="True" />
</i:EventTrigger>
<i:EventTrigger EventName="PreviewKeyDown">
<cmd:EventToCommand Command="{Binding Mode=OneWay, Path=PreviewKeyDownCommand}" PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
答案 2 :(得分:0)
由于这种输入的性质,我建议您创建一些CustomControl
,更具体地说是一个TextBox
,它可以限制Input
并将Text
转换为相应的值-> a GramTextBox
。
GramTextBox
有一个称为DependencyProperty
的{{1}},它代表输入的Gram
的值,并且可以绑定到Text
(注意:绑定必须包含ViewModel
,因为Mode=TwoWay
试图更新绑定的GramTextBox
)。
代码
Source
用法
将此public sealed class GramTextBox : TextBox
{
//Constructor
public GramTextBox() : base()
{
Text = "0g"; //Initial value
TextChanged += OnTextChanged;
DataObject.AddPastingHandler(this, OnPaste);
}
//Style override (get the Style of a TextBox for the GramTextBox)
static GramTextBox()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(GramTextBox), new FrameworkPropertyMetadata(typeof(TextBox)));
}
//Define a DependencyProperty to make it bindable (dont forget 'Mode=TwoWay' due the bound value is updated from this GramTextBox)
[Category("Common"), Description("Converted double value from the entered Text in gram")]
[Browsable(true)]
[Bindable(true)]
public double Gram
{
get { return (double)GetValue(PathDataProperty); }
set { SetCurrentValue(PathDataProperty, value); }
}
public static DependencyProperty PathDataProperty = DependencyProperty.Register("Gram", typeof(double), typeof(GramTextBox), new PropertyMetadata(0d));
//Extract the Gram value when Text has changed
private void OnTextChanged(object sender, TextChangedEventArgs e)
{
ExtractGram(Text);
}
//Suppress space input
protected override void OnPreviewKeyDown(KeyEventArgs e)
{
e.Handled = e.Key == Key.Space;
}
//Check text inputs
protected override void OnPreviewTextInput(TextCompositionEventArgs e)
{
e.Handled = !IsValidText(Text.Insert(CaretIndex, e.Text));
}
//check paste inputs
private void OnPaste(object sender, DataObjectPastingEventArgs e)
{
//Check if pasted object is string
if(e.SourceDataObject.GetData(typeof(string)) is string text)
{
//Check if combined string is valid
if(!IsValidText(Text.Insert(CaretIndex, text))) { e.CancelCommand(); }
}
else { e.CancelCommand(); }
}
//Check valid format for extraction (supports incomplete inputs like 0.m -> 0g)
private bool IsValidText(string text)
{
return Regex.IsMatch(text, @"^([0-9]*?\.?[0-9]*?m?g?)$");
}
//Extract value from entered string
private void ExtractGram(string text)
{
//trim all unwanted characters (only allow 0-9 dots and m or g)
text = Regex.Replace(text, @"[^0-9\.mg]", String.Empty);
//Expected Format -> random numbers, dots and couple m/g
//trim all text after the letter g
text = text.Split('g')[0];
//Expected Format -> random numbers, dots and m
//trim double dots (only one dot is allowed)
text = Regex.Replace(text, @"(?<=\..*)(\.)", String.Empty);
//Expected Format -> random numbers with one or more dots and m
//Check if m is at the end of the string to indicate milli (g was trimmed earlier)
bool isMilli = text.EndsWith("m");
//Remove all m, then only a double number should remain
text = text.Replace("m", String.Empty);
//Expected Format -> random numbers with possible dot
//trim all leading zeros
text = text.TrimStart(new char[] { '0' });
//Expected Format -> random numbers with possible dot
//Check if dot is at the beginning
if (text.StartsWith(".")) { text = $"0{text}"; }
//Expected Format -> random numbers with possible dot
//Check if dot is at the end
if (text.EndsWith(".")) { text = $"{text}0"; }
//Expected Format -> random numbers with possible dot
//Try to convert the remaining String to a Number, if it fails -> 0
Double.TryParse(text, out double result);
//Update Gram Property (divide when necessary)
Gram = (isMilli) ? result / 1000d : result;
}
}
放在Class
中,然后在YOURNAMESPACE
中添加名称空间别名
XAML
现在xmlns:cc="clr-namespace:YOURNAMESPACE"
可以这样使用
GramTextBox
每次<cc:GramTextBox Gram="{Binding VMDoubleProperty, Mode=TwoWay}" ... />
中的Property
更改时(例如,来自键盘/粘贴等的有效输入),它将更新ViewModel
中绑定的Text
。
注释
意图是像GramTextBox
,.00g
,0.0m
这样的废话将.mg
Gram
设置为Property
(像后备值一样) )。
个人笔记
感谢@Pavel提供0
修改
要在PasteHandler
中使用此GramTextBox
,可以覆盖DataGrid
中的CellTemplate
:
Column
答案 3 :(得分:-1)
为防止用户输入数字,您必须使用PrevieTextInput事件,为此创建一个自定义控件是有意义的。下面几行内容将阻止用户输入数字以外的任何内容
<Grid>
<TextBox Text="{Binding Text}" PreviewTextInput="TextBox_PreviewTextInput"/>
<TextBlock HorizontalAlignment="Right" Margin="5,0">g</TextBlock>
</Grid>
private void TextBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
var tb = sender as TextBox;
e.Handled = !double.TryParse(tb.Text+e.Text, out double d);
}
P.S。如果您不喜欢使用try Catch,则可以为此使用正则表达式