【发布时间】:2016-03-08 01:46:12
【问题描述】:
我有一个名为 DoubleNumericBox 的自定义控件,它验证并接受用户输入,例如 23,00、0,9、23.900,01、34... 等。
当我尝试将某些东西绑定到它时,问题就开始了。绑定不够可靠,有时控件不会显示新值,但如果我再设置一次DataContext,它将设置值,等等。
所以,我的自定义属性和事件一定是做错了什么。
自定义属性/事件
- 值:
Double - 最小值:
Double - 最大值:
Double
值已更改:Event
预期行为
- 验证键入的键:数字、逗号和点(小数分隔符和数字分组字形)。我的文化使用
Comma作为小数分隔符。 -
验证整个文本 if(如果数字无效则返回最新的
Value):- 已粘贴文本。
- 失去焦点。
- 验证
Min/Max限制。 - 接受来自
Text或Value的绑定,并验证绑定值。
代码
public class DoubleNumericBox : TextBox
{
变量:
public readonly static DependencyProperty MinValueProperty;
public readonly static DependencyProperty ValueProperty;
public readonly static DependencyProperty MaxValueProperty;
属性:
public double MinValue
{
get { return (double)GetValue(MinValueProperty); }
set { SetCurrentValue(MinValueProperty, value); }
}
public double Value
{
get { return (double)GetValue(ValueProperty); }
set
{
SetCurrentValue(ValueProperty, value);
RaiseValueChangedEvent();
}
}
public double MaxValue
{
get { return (double)GetValue(MaxValueProperty); }
set { SetCurrentValue(MaxValueProperty, value); }
}
事件:
public static readonly RoutedEvent ValueChangedEvent;
public event RoutedEventHandler ValueChanged
{
//Provide CLR accessors for the event
add { AddHandler(ValueChangedEvent, value); }
remove { RemoveHandler(ValueChangedEvent, value); }
}
public void RaiseValueChangedEvent()
{
var newEventArgs = new RoutedEventArgs(ValueChangedEvent);
RaiseEvent(newEventArgs);
}
构造函数/覆盖:
static DoubleNumericBox()
{
MinValueProperty = DependencyProperty.Register("MinValue", typeof(double), typeof(DoubleNumericBox), new FrameworkPropertyMetadata(0D));
ValueProperty = DependencyProperty.Register("Value", typeof(double), typeof(DoubleNumericBox), new FrameworkPropertyMetadata(0D, ValueCallback));
MaxValueProperty = DependencyProperty.Register("MaxValue", typeof(double), typeof(DoubleNumericBox), new FrameworkPropertyMetadata(Double.MaxValue));
ValueChangedEvent = EventManager.RegisterRoutedEvent("ValueChanged", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(DoubleNumericBox));
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
PreviewTextInput += DoubleNumericBox_PreviewTextInput;
ValueChanged += DoubleNumericBox_ValueChanged;
TextChanged += DoubleNumericBox_TextChanged;
LostFocus += DoubleNumericBox_LostFocus;
AddHandler(DataObject.PastingEvent, new DataObjectPastingEventHandler(PastingEvent));
}
事件:
private static void ValueCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var textBox = d as DoubleNumericBox;
if (textBox == null) return;
//textBox.Text = String.Format("{0:###,###,##0.0###}", textBox.Value);
textBox.RaiseValueChangedEvent();
}
private void DoubleNumericBox_ValueChanged(object sender, RoutedEventArgs e)
{
var textBox = sender as DoubleNumericBox;
if (textBox == null) return;
ValueChanged -= DoubleNumericBox_ValueChanged;
TextChanged -= DoubleNumericBox_TextChanged;
if (Value > MaxValue)
Value = MaxValue;
else if (Value < MinValue)
Value = MinValue;
textBox.Text = Text = String.Format("{0:###,###,##0.0###}", Value);
ValueChanged += DoubleNumericBox_ValueChanged;
TextChanged += DoubleNumericBox_TextChanged;
}
private void DoubleNumericBox_TextChanged(object sender, TextChangedEventArgs e)
{
var textBox = sender as TextBox;
if (textBox == null) return;
if (String.IsNullOrEmpty(textBox.Text)) return;
if (IsTextDisallowed(textBox.Text)) return;
ValueChanged -= DoubleNumericBox_ValueChanged;
var newValue = Convert.ToDouble(textBox.Text);
if (newValue > MaxValue)
Value = MaxValue;
else if (newValue < MinValue)
Value = MinValue;
else
{
Value = newValue;
}
ValueChanged += DoubleNumericBox_ValueChanged;
}
private void DoubleNumericBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
if (String.IsNullOrEmpty(e.Text))
{
e.Handled = true;
return;
}
//Only Numbers, comma and points.
if (IsEntryDisallowed(sender, e.Text))
{
e.Handled = true;
}
}
private void PastingEvent(object sender, DataObjectPastingEventArgs e)
{
if (e.DataObject.GetDataPresent(typeof(String)))
{
var text = (String)e.DataObject.GetData(typeof(String));
if (IsTextDisallowed(text))
{
e.CancelCommand();
}
}
else
{
e.CancelCommand();
}
}
private void DoubleNumericBox_LostFocus(object sender, RoutedEventArgs e)
{
TextChanged -= DoubleNumericBox_TextChanged;
Text = String.Format("{0:###,###,##0.0###}", Value);
TextChanged += DoubleNumericBox_TextChanged;
}
方法:
private bool IsEntryDisallowed(object sender, string text)
{
var regex = new Regex(@"^[0-9]|\.|\,$");
if (regex.IsMatch(text))
{
return !CheckPontuation(sender, text);
}
//Not a number or a Comma/Point.
return true;
}
private bool IsTextDisallowed(string text)
{
var regex = new Regex(@"^((\d+)|(\d{1,3}(\.\d{3})+)|(\d{1,3}(\.\d{3})(\,\d{3})+))((\,\d{4})|(\,\d{3})|(\,\d{2})|(\,\d{1})|(\,))?$");
return !regex.IsMatch(text); //\d+(?:,\d{1,2})?
}
private bool CheckPontuation(object sender, string next)
{
var textBox = sender as TextBox;
if (textBox == null) return true;
if (Char.IsNumber(next.ToCharArray()[0]))
return true;
if (next.Equals("."))
{
var textAux = textBox.Text;
if (!String.IsNullOrEmpty(textBox.SelectedText))
textAux = textAux.Replace(textBox.SelectedText, "");
//Check if the user can add a point mark here.
var before = textAux.Substring(0, textBox.SelectionStart);
var after = textAux.Substring(textBox.SelectionStart);
//If no text, return true.
if (String.IsNullOrEmpty(before) && String.IsNullOrEmpty(after)) return true;
if (!String.IsNullOrEmpty(before))
{
if (before.Contains(',')) return false;
if (after.Contains("."))
{
var split = before.Split('.');
if (split.Last().Length != 3) return false;
}
}
if (!String.IsNullOrEmpty(after))
{
var split = after.Split('.', ',');
if (split.First().Length != 3) return false;
}
return true;
}
//Only one comma.
if (next.Equals(","))
{
return !textBox.Text.Any(x => x.Equals(','));
}
return true;
}
}
你们能帮我把这个自定义控件做得更好吗?
【问题讨论】:
-
here is a library that does similar things,抓住你需要的东西或使用 nuget。
标签: wpf data-binding double custom-controls