c# - WPF:创建从 TextBox 派生的“DoubleBox”
问题描述
我希望有一个类似于 aTextBox
但具有附加功能的 WPF 控件,以确保仅接受特定范围内的数字。我想出了以下DoubleBox
控件:
public class DoubleBox : TextBox
{
public static readonly DependencyProperty MinProperty = DependencyProperty.Register(nameof(Min), typeof(double), typeof(DoubleBox), new PropertyMetadata(double.NegativeInfinity, OnMinPropertyChanged));
public static readonly DependencyProperty MaxProperty = DependencyProperty.Register(nameof(Max), typeof(double), typeof(DoubleBox), new PropertyMetadata(double.PositiveInfinity, OnMaxPropertyChanged));
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(nameof(Value), typeof(double), typeof(DoubleBox), new PropertyMetadata(default(double), OnValuePropertyChanged));
private static void OnMinPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
double min = (double)e.NewValue;
double max = (double)d.GetValue(MaxProperty);
double value = (double)d.GetValue(ValueProperty);
if (min > max) throw new Exception("Min value is greater than max value.");
if (value < min) d.SetValue(ValueProperty, min);
}
private static void OnMaxPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
double min = (double)d.GetValue(MinProperty);
double max = (double)e.NewValue;
double value = (double)d.GetValue(ValueProperty);
if (max < min) throw new Exception("Max value is less than min value.");
if (value > max) d.SetValue(ValueProperty, max);
}
private static void OnValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
double min = (double)d.GetValue(MinProperty);
double max = (double)d.GetValue(MaxProperty);
double value = (double)e.NewValue;
if (value < min) d.SetValue(ValueProperty, min);
if (value > max) d.SetValue(ValueProperty, max);
}
public double Min
{
get => (double)GetValue(MinProperty);
set => SetValue(MinProperty, value);
}
public double Max
{
get => (double)GetValue(MaxProperty);
set => SetValue(MaxProperty, value);
}
public double Value
{
get => (double)GetValue(ValueProperty);
set
{
SetValue(ValueProperty, value);
Text = Value.ToString();
}
}
private double previousValue = 0.0;
//protected override void OnInitialized(EventArgs e)
//{
// Text = Value.ToString();
// base.OnInitialized(e);
//}
protected override void OnGotFocus(RoutedEventArgs e)
{
double.TryParse(Text, out previousValue);
base.OnGotFocus(e);
}
protected override void OnLostFocus(RoutedEventArgs e)
{
if (double.TryParse(Text, out double currentValue))
{
if (currentValue < Min) currentValue = Min;
if (currentValue > Max) currentValue = Max;
Value = currentValue;
}
else Value = previousValue;
base.OnLostFocus(e);
}
}
可以像这样在视图中使用:
<DoubleBox Min="{Binding Path=DoubleWithValueOf1, Mode=TwoWay}" Value="{Binding Path=DoubleWithValueOf5, Mode=TwoWay}" />
我的第一个问题是:由于我不习惯 MVVM,这种整体方法是否正确?似乎我为这样一个简单的事情编写了太多代码。
我的主要问题是:即使我绑定Value
了. 我可以通过覆盖该方法来“修复”这个问题(如上所述),但这似乎不正确,我无法在 Visual Studio 的 xaml 设计器中查看实时绑定。DoubleBox
Text
null
Value
string
OnInitialized
解决方案
您的这部分实现是错误的:
public double Value
{
get => (double)GetValue(ValueProperty);
set
{
SetValue(ValueProperty, value);
Text = Value.ToString(); // here
}
}
因为在依赖属性的 CLR 包装器中除了 SetValue 之外不能有其他任何东西。设置属性时并不总是调用设置器,例如通过绑定。WPF 可能会直接调用 SetValue。
将Text
属性分配移动到 PropertyChanged 回调,并添加 CoerceValueCallback,而不是在 PropertyChanged 回调中执行范围检查。
public double Value
{
get => (double)GetValue(ValueProperty);
set => SetValue(ValueProperty, value);
}
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register(
nameof(Value),
typeof(double),
typeof(DoubleBox),
new FrameworkPropertyMetadata(
OnValuePropertyChanged, CoerceValueProperty));
private static object CoerceValueProperty(DependencyObject d, object value)
{
var db = (DoubleBox)d;
var val = (double)value;
return val < db.Min ? db.Min : val > db.Max ? db.Max : val;
}
private static void OnValuePropertyChanged(
DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var db = (DoubleBox)d;
db.Text = db.Value.ToString();
}
推荐阅读
- php - 短代码中的 WooCommerce 评论明星
- java - 如何正确迁移到 Realm 中的嵌入式对象?
- python - 如果变量没有,如何将备用值分配给变量
- css - 修改 GTK 按钮样式/CSS 并立即更新/刷新
- jenkins - 如何在詹金斯管道脚本上禁用轻量级结帐?
- winapi - 如何在 Win32 API 中重现资源管理器按钮
- powershell - 递归地将文件从所有子文件夹移动到目录中的一个子文件夹
- python - Django read file from model, [Errno 2] No such file or directory
- keras - 使用 Keras Tuner 进行超参数调优
- node.js - 如何从 MongoDb 获取云 URL 数据