Silverlight 中的类型安全文本框
在 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
)或附加依赖属性来限制文本框设置不兼容的数据类型。使用哪种方法没有偏好,您可以选择任何一种。