Friday, January 4, 2013

Validate all controls in XAML

Ever thought how to validate all controls in WPF when a user submits a form?

With WPF, you have access to the visual tree and with this in hand, I created my little validator called 'BadAssValidation' :) which enumerates down the tree to find controls to validate.

What I do is that on the submit button in my application, I use the 'Command Parameter' and I pass in the Grid where all the controls that I want to validate exists.
 
<Button Height="23" HorizontalAlignment="Right" Margin="0,0,6,26" Name="btSave" VerticalAlignment="Bottom" Width="75" 
Command="{Binding Path=SaveOrderCommand}" CommandParameter="{Binding ElementName=OrderGrid}" Content="Save" />

Note that each individual control needs to have been bound to their own validator in order for the BadAssValidation to check if they have erroros.

<Binding.ValidationRules>
<validate:valMXvarchar300/>
</Binding.ValidationRules>

In the View Model, I now run the BadAssValidation on the Grid. The validator will enumerate through the visual tree to find matching controls. It will then check if any of these controls have failed validation.

'Validate with BadAssValidationDim validationObj As New Validator.BadAssValidation
If Not validationObj.enumVisual(param) Then
...


And here is the BadAssValidator class:

  Public Class BadAssValidation
  
     Private hasErrors As Boolean = False
  
     ''' <summary>
  
     ''' <para>Validation for all textboxes and comboboxes on UserControl.
  
     ''' </para>
  
     ''' <para>This method loops through textboxes and comboboxes on usercontrol
  
     ''' and calls their Binding validation rule if one exists.</para>
  
     ''' </summary>
  
     ''' <param name="visual">Visual</param>
  
     ''' <returns>Boolean: True if all controls pass validation : else false.</returns>
  
     ''' <remarks></remarks>
  
     Public Function enumVisual(ByVal visual As Visual) As Boolean
  
       Try
  
         ''Count children in visual tree node
  
         Dim j As Integer = VisualTreeHelper.GetChildrenCount(visual) - 1
  
         ''Loop through visual child
  
         For i As Integer = 0 To j
  
           Dim childvisual As Visual = CType(VisualTreeHelper.GetChild(visual, i), Visual)
  
           ''Check if child has a name
  
           If Not childvisual.DependencyObjectType.Name Is Nothing OrElse Not childvisual.DependencyObjectType.Name = "" Then
  
             ''Check if child is of 'TextBox'
  
             If childvisual.DependencyObjectType.Name = "TextBox" Then
  
               Dim txtbox As TextBox = childvisual
  
               ''Update source of control
  
               If Not txtbox.GetBindingExpression(TextBox.TextProperty) Is Nothing Then txtbox.GetBindingExpression(TextBox.TextProperty).UpdateSource()
  
               ''Check if control has errors
  
               If System.Windows.Controls.Validation.GetHasError(txtbox) Then hasErrors = True
  
               'for testing, need to create a new object before use   ': st.Append(txtbox.Name & vbNewLine) : MessageBox.Show(st.ToString)
  
             End If
  
             ''Check if child is of 'ComboBox'
  
             If childvisual.DependencyObjectType.Name = "ComboBox" Then
  
               Dim combox As ComboBox = childvisual
  
               ''Update source of control --Tag
  
               If Not combox.GetBindingExpression(ComboBox.TagProperty) Is Nothing Then combox.GetBindingExpression(ComboBox.TagProperty).UpdateSource()
  
               ''Update source of control --SelectedValue
  
               If Not combox.GetBindingExpression(ComboBox.SelectedValueProperty) Is Nothing Then combox.GetBindingExpression(ComboBox.SelectedValueProperty).UpdateSource()
  
               ''Check if control has errors
  
               If System.Windows.Controls.Validation.GetHasError(combox) Then hasErrors = True
  
               'for testing, need to create a new object before use   ': st.Append(combox.Name & vbNewLine) : MessageBox.Show(st.ToString)
  
             End If
  
             ''Check if child is of 'DatePicker'
  
             If childvisual.DependencyObjectType.Name = "DatePicker" Then
  
               Dim dPicker As DatePicker = childvisual
  
               ''Update source of control
  
               If Not dPicker.GetBindingExpression(DatePicker.SelectedDateProperty) Is Nothing Then dPicker.GetBindingExpression(DatePicker.SelectedDateProperty).UpdateSource()
  
               ''Check if control has errors
  
               If System.Windows.Controls.Validation.GetHasError(dPicker) Then hasErrors = True
  
               'for testing, need to create a new object before use   ': st.Append(txtbox.Name & vbNewLine) : MessageBox.Show(st.ToString)
  
             End If
  
           End If
  
           enumVisual(childvisual)
  
         Next i
  
       Catch ex As Exception
  
         Dim resultObj As New Results
  
         resultObj.displayResult(ex)
  
       End Try
  
       Return hasErrors
  
     End Function
  
     'Private st As System.Text.StringBuilder
  
   End Class
  
   Public Class val0ApprovedRegions
  
     Inherits ValidationRule
  
     Public Overrides Function Validate(ByVal value As Object, ByVal cultureInfo As System.Globalization.CultureInfo) As System.Windows.Controls.ValidationResult
  
       Dim valChar As Char = CChar(value)
  
       If valChar = "T" Then
  
         Return New ValidationResult(False, "Approved regions must be selected.")
  
       Else
  
         Return New ValidationResult(True, Nothing)
  
       End If
  
     End Function
  
   End Class  

No comments:

Post a Comment