我正在使用 Dot Net 8.0,并且创建了一个 WPF 应用程序。 在窗口上,我希望有一个绑定到 Float 的文本框控件,以便用户可以查看和修改它。 我已经尝试了很多方法,但无法获得可以输入小数点(句点)的绑定文本框的版本。
我创建了一个简单的项目,它有一个简单的窗口,其中包含我尝试过的几个示例。 每个文本框正确显示默认浮点值。 在每个文本框中,我一致地执行以下步骤:
下面,我记录一下我的步骤和结果。 接下来,我将提供我的简单示例,该示例仅包含两个文件。 如果有人觉得精力充沛,你应该能够重现我的作品。
解决这个问题有两种方法,一种是防止输入无效字符或在无效位置输入有效字符。 另一种是允许使用任何字符,但在结果无效时用红色边框指示。 此时,如果可以让我输入小数位,我将采用任一方法。
如果有人有想要分享的可行示例,我会欢迎。
A. Simple TextBox bound to a float
a. Cannot enter a decimal point
b. After backspace output says “ConvertBack cannot convert value '' (type 'String')” referring to an empty string
c. After entering the ‘-‘, the TextBox contains ‘-‘ and the output says “ConvertBack cannot convert value '-' (type 'String')”
d. After entering the ‘8’, the TextBox contains ‘-8’ and there is no output
e. After entering the ‘.’, the TextBox does not change and there is no output
f. After entering the ‘9’, the TextBox contains ‘-89’ and there is no output
B. Simple TextBox bount to a float with a converter
a. Cannot enter a decimal point
b. After entering the backspace, the TextBox contains ‘0’ and there is no output
c. After entering the ‘-‘, the TextBox contains ‘-0’ and there is no output
d. After entering the ‘8’, the TextBox contains ‘-80’ and there is no output
e. After entering the ‘.’, the TextBox contains ‘-8’ and there is no output
f. After entering the ‘9’, the TextBox contains ‘-89’ and there is no output
C. TextBox bount to a float with two events
a. Cannot enter a decimal point nor the negative sign
b. After entering the backspace, the TextBox is empty, and the output says “ConvertBack cannot convert value '' (type 'String')” referring to an empty string
c. After entering the ‘-‘, the TextBox is empty and the output says:
i. PreviewTextInput - TextBox Text:
ii. PreviewTextInput - New Text: -
iii. PreviewTextInput - Is Valid: False
d. After entering the ‘8’, the TextBox contains ‘8’ and the output says:
i. PreviewTextInput - TextBox Text:
ii. PreviewTextInput - New Text: 8
iii. PreviewTextInput - Is Valid: True
e. After entering the ‘.’, the TextBox still contains ‘8’ and the output says:
i. PreviewTextInput - TextBox Text: 8
ii. PreviewTextInput - New Text: 8.
iii. PreviewTextInput - Is Valid: True
iv. **NOTE:** The decimal does make it into the New Text field but is not in the TextBox on the screen
f. After entering the ‘9’, the TextBox contains ‘89’ and the output says:
i. PreviewTextInput - TextBox Text: 8
ii. PreviewTextInput - New Text: 89
iii. PreviewTextInput - Is Valid: True
g. **NOTE:** none of the debug messages in the TextInput event handler write anything. In fact, if I put a breakpoint in the handler, it never fires.
D. Custom TextBox bound to a float overriding OnTextInput
a. Cannot enter a decimal point nor the negative sign
b. After entering the backspace, the TextBox is clear and the output says “ConvertBack cannot convert value '' (type 'String')” referring to the empty string.
c. After entering the ‘8’, the TextBox contains ‘88’ and the output says:
i. PreviewTextInput - TextBox Text:
ii. PreviewTextInput - New Text: 8
iii. PreviewTextInput - Is Valid: True
d. After entering the ‘.’, the TextBox still contains ‘88’ and the output says:
i. PreviewTextInput - TextBox Text: 88
ii. PreviewTextInput - New Text: 88.
iii. PreviewTextInput - Is Valid: True
iv. **NOTE:** the decimal point shows up in the New Text field, but not in the TextBox on the screen
e. After entering the ‘9’, the TextBox contains ‘8899’ and the output says:
i. PreviewTextInput - TextBox Text: 88
ii. PreviewTextInput - New Text: 889
iii. PreviewTextInput - Is Valid: True
f. **NOTE:** the digits are doubled and the TextInput event handler is not fired
E. TextBox unbound
a. I can enter anything I wish but the TextBox is not bound to a float
b. I would have to handle all the validation in the event handlers and update the backing float myself. This would be a lot of legwork for something that should be simple.
c. **`NOTE:`** This is the ONLY option where I can enter a decimal point
主窗口.xaml
<Window x:Class="POC_WpfFloatingPointTextBox.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:POC_WpfFloatingPointTextBox"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<local:FloatToStringConverter x:Key="FloatToStringConverter"/>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel Orientation="Vertical" HorizontalAlignment="Left">
<Label Content="Simple TextBox bound to a float"/>
<TextBox
x:Name="txbA"
Height="20" Width="200"
Margin="10"
Text="{Binding FloatA, UpdateSourceTrigger=PropertyChanged}"/>
<Label Content="Simple TextBox bound to a float with a converter"/>
<TextBox
x:Name="txbB"
Height="20" Width="200"
Margin="10"
Text="{Binding FloatB, Converter={StaticResource FloatToStringConverter}, UpdateSourceTrigger=PropertyChanged}"/>
<Label Content="TextBox bound to a float with two events"/>
<TextBox
x:Name="txbC"
Height="20" Width="200"
Margin="10"
PreviewTextInput="TextBox_PreviewTextInput"
TextInput="TextBox_TextInput"
Text="{Binding FloatC, UpdateSourceTrigger=PropertyChanged}"/>
<Label Content="Custom TextBox bound to a float overriding OnTextInput"/>
<local:CustomTextBox
x:Name="txbD"
Height="20" Width="200"
Margin="10"
PreviewTextInput="TextBox_PreviewTextInput"
TextInput="TextBox_TextInput"
Text="{Binding FloatD, UpdateSourceTrigger=PropertyChanged}"/>
<Label Content="TextBox unbounded"/>
<TextBox
x:Name="txbE"
Height="20" Width="200"
Margin="10"/>
</StackPanel>
</Grid>
</Window>
MainWindow.xaml.cs
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
namespace POC_WpfFloatingPointTextBox
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window, INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
}
#region Properties
private float _floatA = -1.2f;
public float FloatA
{
get { return _floatA; }
set { _floatA = value; OnPropertyChanged(nameof(FloatA)); }
}
private float _floatB = -2.3f;
public float FloatB
{
get { return _floatB; }
set { _floatB = value; OnPropertyChanged(nameof(FloatB)); }
}
private float _floatC = -3.4f;
public float FloatC
{
get { return _floatC; }
set { _floatC = value; OnPropertyChanged(nameof(FloatC)); }
}
private float _floatD = -4.5f;
public float FloatD
{
get { return _floatD; }
set { _floatD = value; OnPropertyChanged(nameof(FloatD)); }
}
#endregion Properties
#region Private Methods
private void TextBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
// Allow only digits, control characters, and decimal points
TextBox? textBox = sender as TextBox;
if (textBox != null)
{
string newText = textBox.Text.Insert(textBox.CaretIndex, e.Text);
Debug.WriteLine($"PreviewTextInput - TextBox Text: {textBox.Text}");
Debug.WriteLine($"PreviewTextInput - New Text: {newText}");
bool isValid = float.TryParse(newText, NumberStyles.Float, CultureInfo.InvariantCulture, out _);
Debug.WriteLine($"PreviewTextInput - Is Valid: {isValid}");
e.Handled = !isValid;
}
}
private void TextBox_TextInput(object sender, TextCompositionEventArgs e)
{
TextBox? textBox = sender as TextBox;
if (textBox != null)
{
string newText = textBox.Text.Insert(textBox.CaretIndex, e.Text);
Debug.WriteLine($"TextInput - TextBox Text: {textBox.Text}");
Debug.WriteLine($"TextInput - New Text: {newText}");
bool isValid = float.TryParse(newText, NumberStyles.Float, CultureInfo.InvariantCulture, out _);
Debug.WriteLine($"TextInput - Is Valid: {isValid}");
if (isValid)
{
textBox.Text = newText;
textBox.CaretIndex = newText.Length;
}
e.Handled = !isValid;
}
}
#endregion Private Methods
#region INotifyPropertyChanged
public event PropertyChangedEventHandler? PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChangedEventHandler? changed = PropertyChanged;
if (changed == null)
{
return;
}
changed.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion INotifyPropertyChanged
}
public class FloatToStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is float f)
{
return f.ToString("0.##");
}
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (float.TryParse(value as string, out float result))
{
return result;
}
return 0f; // or other default value
}
}
public class CustomTextBox : TextBox
{
protected override void OnTextInput(TextCompositionEventArgs e)
{
base.OnTextInput(e);
TextBox? textBox = this;
if (textBox != null)
{
string newText = textBox.Text.Insert(textBox.CaretIndex, e.Text);
if (float.TryParse(newText, NumberStyles.Float, CultureInfo.InvariantCulture, out _))
{
textBox.Text = newText;
textBox.CaretIndex = newText.Length;
}
}
}
}
}
在窗口上我希望有一个绑定到 Float 的文本框控件 这样用户就可以看到并修改它。我尝试了很多事情但不能 获取绑定的文本框版本,我可以在其中输入 小数点(句点)。
此示例按要求执行;挂钩 PreviewKeyDown;并有一个“确认”按钮来显示绑定的浮动。
XAML:
<StackPanel Orientation="Horizontal"
VerticalAlignment="Center"
HorizontalAlignment="Center">
<TextBox x:Name="UxText"
Text="{Binding Float1}"
Width="96"
PreviewKeyDown="UxText_PreviewKeyDown" />
<Button x:Name="UxConfirm"
Content="Confirm"
Margin="8,0,0,0"
Click="UxConfirm_Click" />
</StackPanel>
背后代码:
public partial class MainWindow : Window {
public float Float1 { get; set; } = 1.98f;
/// <summary>
///
/// </summary>
public MainWindow() {
InitializeComponent();
this.DataContext = this;
}
/// <summary>
///
/// </summary>
private void UxText_PreviewKeyDown( object sender, KeyEventArgs e ) {
// Accept "one" decimal point.
if ( e.Key == Key.OemPeriod ) {
var tb = sender as TextBox;
if ( tb.Text.Contains( "." ) ) {
e.Handled = true; // Reject.
}
return; // Accept.
}
// Accept "one" minus sign ... "first" key only.
if ( e.Key == Key.OemMinus ) {
var tb = sender as TextBox;
if ( tb.Text.Contains( "-" ) || tb.Text.Length > 0 ) {
e.Handled = true; // Reject.
}
return; // Accept.
}
// Accept digits.
if ( e.Key >= Key.D0 && e.Key <= Key.D9 ) {
return; // Accept.
}
if ( e.Key >= Key.NumPad0 && e.Key <= Key.NumPad9 ) {
return; // Accept.
}
// Accept edit keys.
switch ( e.Key ) {
case Key.Back:
case Key.Tab:
case Key.Insert:
case Key.Delete:
case Key.End:
case Key.Home:
case Key.Left:
case Key.Up:
case Key.Right:
case Key.Down:
return; // Accept.
}
// Reject key.
e.Handled = true;
return;
}
/// <summary>
///
/// </summary>
private void UxConfirm_Click( object sender, RoutedEventArgs e ) {
MessageBox.Show( $"Float1: {this.Float1}" );
}
} // end class.