65.9K
CodeProject 正在变化。 阅读更多。
Home

Silverlight 中的类型安全文本框

starIconemptyStarIconemptyStarIconemptyStarIconemptyStarIcon

1.00/5 (1投票)

2011 年 12 月 7 日

CPOL

2分钟阅读

viewsIcon

15704

downloadIcon

120

在 Silverlight 中创建一个类型安全的文本框,或者为文本框提供类型安全行为。

引言

在开发 Silverlight 应用程序时,我们经常会遇到将文本框绑定到各种类型数据的需求。由于文本框允许输入字符串,我们可能无法在 ViewModel(或绑定的属性)中接收到数据,因为源数据类型不兼容。

例如,一个 TextBox 被绑定到一个 int 类型的源属性。如果用户输入非数字字符,源属性将永远无法接收到值,导致视图和 ViewModel 中的值不一致。

本文解释了解决此问题的两种方法。

背景

本文处理的是绑定目标类型和源类型之间的格式不兼容问题。以下描述的方法不会限制用户输入不兼容的数据,而是确保使用 TypeConverterAttribute 检查内容兼容性。本文假定读者具有数据绑定的基本知识,并且熟悉源属性和目标属性的概念。

使用代码

在 Silverlight/WPF 中,我们拥有属性来提供有关数据的额外信息。本文利用这种属性方法来处理类型转换。以下代码片段展示了一个整数数据类型的示例类型转换器

public class IntTypeConverter : TypeConverter
{
    public override object ConvertFrom(ITypeDescriptorContext context, 
        System.Globalization.CultureInfo culture, object value)
    {
        if (value == null)
        {
            throw new FormatException(string.Format(
                "Null value not supported for {0}", typeof(int)));
        }
        try
        {
            return int.Parse(value.ToString());
        }
        catch
        {
            return default(int);
        }
    }
}

一旦声明了 TypeAttribute,就必须将其与将被绑定到 TextBox 的源属性关联。以下代码片段将帮助您实现这一点。

[TypeConverter(typeof(IntTypeConverter))]
public int Field1
{
    get
    {
        return field1;
    }
    set
    {
        field1 = value;
        OnPropertyChanged("Field1");
    }
}

方法 1:TypeSafeTextBox

TypeSafeTextBox 订阅了失去焦点事件,并使用 BindingExpression 提取源属性及其 TypeConverter 信息。一旦收到这些信息,它就会检查转换。

public class TypeSafeTextBox : TextBox
{
    public TypeSafeTextBox()
    {
       this.LostFocus += new RoutedEventHandler(TypeSafeTextBox_LostFocus);
    }
    void TypeSafeTextBox_LostFocus(object sender, RoutedEventArgs e)
    {
        try
        {
            BindingExpression textBindingExpression = this.GetBindingExpression(TextBox.TextProperty);
            if (textBindingExpression != null && textBindingExpression.ParentBinding != null)
            {
                PropertyInfo propertyInfo = GetPropertyInfo(
                    textBindingExpression.DataItem.GetType(), 
                    textBindingExpression.ParentBinding.Path.Path);
                if (propertyInfo != null)
                {
                    var attributes = propertyInfo.GetCustomAttributes(typeof(TypeConverterAttribute), true);
                    if (attributes != null && attributes.Length > 0)
                    {
                        var convAttribute = attributes[0] as TypeConverterAttribute;
                        if (convAttribute != null)
                        {
                            var converterType = Type.GetType(convAttribute.ConverterTypeName, false);
                            if (converterType != null)
                            {
                                TypeConverter conv = (Activator.CreateInstance(converterType) as TypeConverter);
                                if (conv != null)
                                {
                                    object value = conv.ConvertFrom(null, Thread.CurrentThread.CurrentUICulture,
                                    this.Text);
                                    this.Text = value.ToString();
                                }
                            }
                        }
                    }
                }
            }
        }
        catch (Exception)
        {
          this.SetValue(TextProperty, DependencyProperty.UnsetValue);
        }
    }
    
    private PropertyInfo GetPropertyInfo(Type type, string path)
    {
        if (path.Contains("."))
        {
            string startPath = path.Substring(0, path.IndexOf("."));
            string endPath = path.Substring(path.IndexOf(".") + 1);
            return GetPropertyInfo(type.GetProperty(startPath).PropertyType, endPath);
        }
        return type.GetProperty(path);
    }
}

上面的代码描述了文本框失去焦点时如何执行类型转换,限制用户只能输入有效数据,否则设置默认数据类型值。上面的代码中的 GetPropertyInfo 帮助遍历复杂的源绑定。

方法 2:附加依赖属性

我们可以通过利用附加依赖属性功能来避免创建继承的 TypeSafeTextBox 控件。以下代码片段展示了如何创建带有类型安全检查的附加属性。

public static readonly DependencyProperty CheckProperty = 
   DependencyProperty.RegisterAttached("Check",typeof (bool),
   typeof (TypeSafeAttachment),
   new PropertyMetadata(false,new PropertyChangedCallback(CheckChanged)));

public static void SetCheck(UIElement element, bool value)
{
  element.SetValue(CheckProperty, value);
}

public static bool GetCheck(UIElement element)
{
  return (bool) element.GetValue(CheckProperty);
}

private static void CheckChanged(DependencyObject sender, 
                    DependencyPropertyChangedEventArgs e)
{
  TextBox textBox = sender as TextBox;
  if(textBox!=null)
  {
    textBox.LostFocus -= new RoutedEventHandler(TypeSafeTextBox_LostFocus);
    textBox.LostFocus += new RoutedEventHandler(TypeSafeTextBox_LostFocus);
  }
}

下面的示例源代码展示了如何在 XAML 中使用这两种方法进行绑定

// Using attached property 
<TextBox TextBoxDemo:TypeSafeAttachment.Check="True" 
   Text="{Binding Field1, Mode=TwoWay}" Margin="5" /></TextBox />
          
//  Using TypeSafeTextBox 
<TypeSafeTextBox Text="{Binding Field1, Mode=TwoWay}" Margin="5" />
</TypeSafeTextBox />

//  Using TypeSafeTextBox for nested binding source Data.Field1
<TypeSafeTextBox Text="{Binding Data.Field1, Mode=TwoWay}" Margin="5" />

结论

如上所述,您可以使用派生的文本框(TypeSafeTextBox)或附加依赖属性来限制文本框设置不兼容的数据类型。使用哪种方法没有偏好,您可以选择任何一种。

© . All rights reserved.