views:

132

answers:

3

Hi All, In my model I have lots of properties for different objects and I'm checking the value while setting value for object and if the value is not accepted I will throw an exception this was working perfect with windows forms propertygrid but now I'm trying to design a new interface using WPF . in WPF when I bound a property to a control like textbox ,when the value is changed I don't know how to handle the exception and show the error message . example :

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 )); 
                }
            }
        }

and the wpf part is like :

<TextBox TextWrapping="Wrap" x:Name="ConnectionStringTextBox" Text="{Binding Path=ConnectionString, Mode=TwoWay}"/>

is there anyway when the value in textbox is changed check if the model has thrown an exception and then show the exception.message to user ?

thanks

A: 

You could always use a messagebox to display the exception.

Pete OHanlon
+2  A: 

Take a look at binding validation. The Binding class has a ValidationRules collection, to which you can add an ExceptionValidationRule.

HTH,
Kent

Kent Boogaart
+1  A: 

Kent is absolutely correct about using ValidationRule and ExceptionValidationRule. However you will find this solution very unpleasant to use for your situation where you have a lot of bindings to fields like this. In many places you will be replacing something like this:

<TextBox Text="{Binding Value}" />

with this:

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

Because this is so unwieldly, I like to create an inherited attached property that automatically applies validation rules, so all I have to say is:

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

My attached property automatically applies validation to every binding in the window, so the individual textboxes don't have to worry about validation.

To do this, I use this general techinique:

  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;
    }
  }

See the MSDN documentation of the Validation class for an example of how to create your errorTemplate resource. Also note that:

  • My ValidationHelper class doesn't prevent you from setting custom Validation.ErrorTemplate values on individual TextBoxes. These will override the ValidationHelper.ErrorTemplate.
  • You can easily add support for controls other than TextBox and properties other than Text
Ray Burns