[英]Can't get DependencyProperty to work
I wrote a small attached property called "IsValid" for the WPF TextBox, like so: 我为WPF TextBox写了一个名为“IsValid”的小附加属性,如下所示:
public enum InputTypes
{
Any,
Integer,
Double,
Float
}
/// <summary>
/// This attached property can be used to validate input for <see cref="TextBox"/>.
/// </summary>
public class IsValid : DependencyObject
{
public static readonly DependencyProperty InputProperty = DependencyProperty.Register(
"Input",
typeof(InputTypes),
typeof(IsValid),
new UIPropertyMetadata(InputTypes.Any, onInput));
public static InputTypes GetInput(DependencyObject d)
{
return (InputTypes)d.GetValue(InputProperty);
}
public static void SetInput(DependencyObject d, InputTypes value)
{
d.SetValue(InputProperty, value);
}
private static void onInput(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var textBox = (TextBox)d;
var value = (InputTypes)e.NewValue;
switch (value)
{
case InputTypes.Any:
textBox.PreviewTextInput -= validateInput;
textBox.PreviewKeyDown -= validateKeyDown;
break;
default:
textBox.PreviewTextInput += validateInput;
textBox.PreviewKeyDown += validateKeyDown;
break;
}
}
private static void validateInput(object sender, TextCompositionEventArgs e)
{
// enforce numeric input when configured ...
var textBox = (TextBox) sender;
var inputTypes = (InputTypes) textBox.GetValue(InputProperty);
foreach (var c in e.Text)
{
switch (inputTypes)
{
case InputTypes.Integer:
if (!char.IsDigit(c))
{
e.Handled = true;
return;
}
break;
case InputTypes.Double:
case InputTypes.Float:
if (!char.IsNumber(c))
{
e.Handled = true;
return;
}
break;
default:
throw new ArgumentOutOfRangeException();
}
}
}
private static void validateKeyDown(object sender, KeyEventArgs e)
{
// block [SPACE] when numeric input is expected ...
var textBox = (TextBox)sender;
var inputTypes = (InputTypes)textBox.GetValue(InputProperty);
if (inputTypes != InputTypes.Any && e.Key == Key.Space)
e.Handled = true;
}
}
End here's how I've used it: 到此为止我是如何使用它的:
<Window x:Class="Spike.Wpf.Controls.TestApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:values="clr-namespace:Spike.Wpf.Controls.Input;assembly=Spike.Wpf.Controls"
Title="MainWindow" Height="350" Width="525">
<Grid>
<TextBox values:IsValid.Input="Double" />
</Grid>
After the initialization (of the DependencyProperty
) none of the methods in IsValid
gets called however. 在初始化(
DependencyProperty
)之后, IsValid
没有任何方法被调用。 What am I missing? 我错过了什么?
Earlier you probably got an error telling you that IsValid
needs to derive from DependecyObject
, so you added that, you should have asked yourself why that is. 早些时候你可能会收到一个错误,告诉你
IsValid
需要从DependecyObject
派生,所以你补充说,你应该问问自己为什么会这样。 The answer is right here: 答案就在这里:
public static readonly DependencyProperty InputProperty = DependencyProperty.Register(...
You try to register a normal property for objects on type IsValid
, change it to RegisterAttached
and it should work. 您尝试为
IsValid
类型的对象注册一个普通属性,将其更改为RegisterAttached
,它应该工作。 (I would also remove the inheritance and make IsValid
a static class) (我也会删除继承并使
IsValid
成为静态类)
Ok, so the core of the problem was trivial (see accepted answer): I needed to call DependencyProperty.RegisterAttached(...)
(as opposed to DependencyProperty.Register(...)
. 好的,所以问题的核心是微不足道的(参见接受的答案):我需要调用
DependencyProperty.RegisterAttached(...)
(而不是DependencyProperty.Register(...)
。
Just wanted to share the result. 只是想分享结果。 I decided to scrap the use of a simple
enum
to specify input type and decided to use markup extensions instead. 我决定废弃使用简单的
enum
来指定输入类型,并决定使用标记扩展。
The attached property implementation now looks like this: 附加的属性实现现在看起来像这样:
public static class IsValid
{
public static readonly DependencyProperty InputProperty = DependencyProperty.RegisterAttached(
"Input",
typeof(IsValidInputExtension),
typeof(IsValid),
new UIPropertyMetadata(onInput));
public static IsValidInputExtension GetInput(DependencyObject d)
{
return (IsValidInputExtension)d.GetValue(InputProperty);
}
public static void SetInput(DependencyObject d, IsValidInputExtension value)
{
d.SetValue(InputProperty, value);
}
private static void onInput(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var textBox = (TextBox)d;
var value = (IsValidInputExtension)e.NewValue;
if (value == null)
{
textBox.PreviewTextInput -= validateInput;
textBox.PreviewKeyDown -= validateKeyDown;
return;
}
textBox.PreviewTextInput += validateInput;
textBox.PreviewKeyDown += validateKeyDown;
}
private static void validateInput(object sender, TextCompositionEventArgs e)
{
// dispatch validation to specified markup class ...
var textBox = (TextBox) sender;
var markup = (IsValidInputExtension)textBox.GetValue(InputProperty);
markup.ValidateInput(sender, e);
}
private static void validateKeyDown(object sender, KeyEventArgs e)
{
// dispatch validation to specified markup class ...
var textBox = (TextBox)sender;
var markup = (IsValidInputExtension)textBox.GetValue(InputProperty);
markup.ValidateKeyDown(sender, e);
}
}
And here's part of the markup extension classes: 这是标记扩展类的一部分:
public abstract class IsValidInputExtension : MarkupExtension
{
internal abstract void ValidateInput(object sender, TextCompositionEventArgs e);
internal abstract void ValidateKeyDown(object sender, KeyEventArgs e);
}
public class NumericExtension : IsValidInputExtension
{
public double Minimum { get; set; }
public double Maximum { get; set; }
public uint Decimals { get; set; }
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
internal override void ValidateInput(object sender, TextCompositionEventArgs e)
{
var textBox = (TextBox) sender;
if (isDecimalSeparator(e.Text) && Decimals == 0)
{
e.Handled = true;
return;
}
// todo: honor Minimum and Maximum ...
}
private static bool isDecimalSeparator(string s)
{
return CultureInfo.CurrentUICulture.NumberFormat.CurrencyDecimalSeparator == s;
}
internal override void ValidateKeyDown(object sender, KeyEventArgs e)
{
// block [SPACE] when numeric input is expected ...
e.Handled = e.Key == Key.Space;
}
}
public class StringExtension : IsValidInputExtension
{
public double MaximumLength { get; set; }
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
internal override void ValidateInput(object sender, TextCompositionEventArgs e)
{
// (nop)
}
internal override void ValidateKeyDown(object sender, KeyEventArgs e)
{
// todo: honor MaximumLength here
}
}
The end result, in XAML, is quite nice and easy to read... 在XAML中,最终结果非常好,易于阅读......
<TextBox v:IsValid.Input="{v:Numeric Minimum=0, Maximum=99, Decimals=0}" />
It all seems to work as I hoped. 这一切似乎都像我希望的那样奏效。 Thanks for all input
感谢所有输入
Cheers 干杯
/Jonas /乔纳斯
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.