Can't get DependencyProperty to work
我为wpf文本框编写了一个名为"isvalid"的小附加属性,如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | 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; } } |
结尾是我如何使用它:
1 2 3 4 5 6 7 8 | <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> |
但是,在初始化(
早些时候,你可能会有一个错误告诉你,
1 | public static readonly DependencyProperty InputProperty = DependencyProperty.Register(... |
您尝试为
好吧,所以问题的核心是微不足道的(见公认的答案):我需要打电话给
只是想分享结果。我决定不再使用简单的
附加属性实现现在如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | 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); } } |
下面是标记扩展类的一部分:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | 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 } } |
最终的结果,在XAML中,是非常好和容易阅读的…
1 | <TextBox v:IsValid.Input="{v:Numeric Minimum=0, Maximum=99, Decimals=0}" /> |
一切似乎如我所愿。谢谢你的所有意见
干杯
乔纳斯