WPF数据绑定监控抛出的异常

4

在我的模型中,我有许多不同对象的属性,并且在设置对象的值时检查该值,如果该值不被接受,我将抛出异常。这在Windows窗体属性网格中运行得很完美,但现在我正在尝试使用WPF设计一个新的界面。

在WPF中,当我将属性绑定到像文本框这样的控件时,当值改变时,我不知道如何处理异常并显示错误消息。

例如:

public string  ConnectionString
        {
            get
            {
                return (_ConnectionString);
            }
            set
            {
                try
                {
                    _ConnectionString  = value ;
                    _SqlConnection = new System.Data.SqlClient.SqlConnection(_ConnectionString);
                    _ConnectionTested = true;
                }
                catch (Exception caught)
                {
                    _ConnectionTested = false;
                    _TableNameTested = false;
                    _FieldNameTested = false;
                    _ConditionTested = false;
                    _ConnectionString = "";
                    //----delete values----
                    ValuesCollection.Clear();
                    throw (new Exception("Can not Open the connection String \nReason : " + caught.Message )); 
                }
            }
        }

而 WPF 部分则如下所示:
<TextBox TextWrapping="Wrap" x:Name="ConnectionStringTextBox" Text="{Binding Path=ConnectionString, Mode=TwoWay}"/>

当文本框中的值改变时,有没有办法检查模型是否抛出异常,然后将异常信息显示给用户?

谢谢。

2个回答

5
肯特在使用ValidationRule和ExceptionValidationRule方面是绝对正确的。但是,在你需要将许多绑定到这样的字段的情况下,你会发现这种解决方案非常不愉快。在许多地方,你将替换以下内容:
<TextBox Text="{Binding Value}" />

使用这个:

<TextBox Validation.ErrorTemplate="{StaticResource errorTemplate}">
  <TextBox.Text>
    <Binding Path="Value">
      <Binding.ValidationRules>
        <ExceptionValidationRule />
      </Binding.ValidationRules>
    </Binding>
  </TextBox.Text>
</TextBox>

由于这样做非常笨重,我喜欢创建一个继承的附加属性来自动应用验证规则,这样我只需要说:

<Window
  ValidationHelper.ErrorTemplate="{StaticResource errorTemplate}"
...
   <TextBox Text="{Binding Value}" />
   <TextBox Text="{Binding OtherValue}" />

我的附加属性会自动对窗口中的每个绑定应用验证,因此单个文本框不必担心验证。

为了实现这一点,我使用以下通用技术:

  public class ValidationHelper : DependencyObject
  {
    [ThreadStatic]
    static List<DependencyObject> _objectsNeedingValidationUpdate;

    public static ControlTemplate GetErrorTemplate(DependencyObject obj) { return (ControlTemplate)obj.GetValue(ErrorTemplateProperty); }
    public static void SetErrorTemplate(DependencyObject obj, ControlTemplate value) { obj.SetValue(ErrorTemplateProperty, value); }
    public static readonly DependencyProperty ErrorTemplateProperty = DependencyProperty.RegisterAttached("ErrorTemplate", typeof(ControlTemplate), typeof(ValidationHelper), new FrameworkPropertyMetadata
    {
      Inherits = true,
      PropertyChangedCallback = (obj, e) =>
        {
          if(e.NewValue)
            if(_objectsNeedingValidationUpdate!=null)
              _objectsNeedingValidationUpdate.Add(obj);
            else
            {
              _objectsNeedingValidationUpdate = new List<DependencyObject>();
              _objectsNeedingValidationUpdate.Add(obj);
              Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Render, new Action(UpdateValidations));
            }
        },
    });

    static void UpdateValidations()
    {
      List<DependencyObject> objects = _objectsNeedingValidationUpdate;
      _objectsNeedingValidationUpdate = null;
      if(objects!=null)
        foreach(DependencyObject obj in objects)
          UpdateValidations(obj);
    }
    static void UpdateValidations(DependencyObject obj)
    {
      // My regular code uses obj.GetLocalValueEnumerator here, but that would require some other complexity
      if(UpdateValidations(obj, TextBox.TextProperty))
        if(Validation.GetErrorTemplate(obj)==null)
          Validation.SetErrorTemplate(obj, ValidationHelper.GetErrorTemplate(obj));
    }
    static bool UpdateValidations(DependencyObject obj, DependencyProperty prop)
    {
      var binding = BindingOperations.GetBinding(obj, prop);
      if(binding!=null &&
        binding.Mode==BindingMode.TwoWay &&
        !binding.ValidationRules.Any(rule => rule is ExceptionValidationRule))
      {
        binding.ValidationRules.Add(new ExceptionValidationRule());
        BindingOperations.SetBinding(obj, prop, binding);  // Required to get new rule to work
        return true;
      }
      return false;
    }
  }

请参考Validation类的MSDN文档,了解如何创建您的errorTemplate资源的示例。另外请注意以下几点:
  • 我的ValidationHelper类不会阻止您在单个TextBox上设置自定义的Validation.ErrorTemplate值。这些将覆盖ValidationHelper.ErrorTemplate。
  • 您可以轻松地为除了TextBox之外的控件和除了Text之外的属性添加支持。

你是唯一一个理解当一行代码被迫转化为9行代码时有多丑陋的人。 - nyconing

3

请看绑定验证Binding类有一个ValidationRules集合,您可以向其中添加一个ExceptionValidationRule


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接