Archive

Posts Tagged ‘Validation’

Checking Property Types Automatically

May 6, 2012 2 comments

It’s been a long time since I have written in this blog. The reason is: there have been many changes in my life during the past two years, and I simply haven’t found the time until now. Anyway, I’ve been checking out things and learning continuously, and I hope to be able to share these things more regularly in the future.

In the last post, Validating Objects with ValidationRules, I proposed a way to define ValidationRules for an object in one location and to set them on all Bindings that need them through a single attached property.

Today, I want to show how to set up basic type checks automatically. The example code is contained in the ValidationExample project in the source code distribution, which you can access through the downloads page.

How WPF handles Type

Normally, if the value entered in a TextBox cannot be converted into the type of the bound property, e.g. if you enter letters into a TextBox expecting an integer value, first of all there will be a runtime exception. By default, WPF will handle this exception, mark the TextBox as having invalid data, and assign a default “Conversion error” error message to it. Since the value cannot be converted, WPF will not update the bound property, which means that it will keep its value unchanged.

The first level of refinement here would be to allow the Binding to display exception messages by setting ValidatesOnExceptions to true. Now, if changing a property causes an exception, Validation.Errors will contain the exception’s message. Now the user will at least know whether the reason for the exception is a type mismatch or a numerical overflow.

Validation as a User Interface Responsibility

Why is this not enough?

Reusable software components like services or libraries need to check their input and throw exceptions if it is invalid. This is how one piece of software tells another piece of software that it has done something wrong.

User Interfaces, which basically translate between human users and the software they want to use, are responsible for making sure that those users only provide valid input values. The best way to do that is to offer only valid options to the user. Where that isn’t possible, validation steps in by providing explicit and meaningful validation messages which tell the users what they have to change in order to achieve the intended result.

As a rule of thumb, I’d say that one should protect one’s services and libraries by throwing exceptions, and that one should use validation in the UI to make sure that those exceptions never get thrown.

So, while exceptions are directed at technically aware developers debugging their software components, validation messages are directed at end users with possibly no technical background at all. For example, the error message you receive when you enter too many digits into the TextBox that validates with exceptions in the example project, reads: “Value was either too large or too small for an Int32”. This is obviously not what one would display to a user who has never heard of Int32.

Therefore, in the user interface, one would need two functionalities:

  1. a way to provide globalized validation messages independent of the system’s exception messages.
  2. a way to keep WPF controls from trying to update properties with values of non-matching type.

The TypeValidationRuleSetter Class

The TypeValidationRuleSetter class is a subclass of the ValidationRuleSetter class I mentioned in the last post. In addition to adding predefined ValidationRules to specified properties on a bound object, it will inspect the object’s type, find out the primitive CLR-type of a bound property, and will add default validation rules for each type which use XAML-configurable validation messages.

At the heart of the TypeValidationRuleSetter class is the following method:

Protected Overrides Sub AttachRules(ByVal base As Object)
    Dim f As FrameworkElement = TryCast(base, FrameworkElement)
    If f IsNot Nothing And ReferenceType IsNot Nothing Then
        If f IsNot Nothing Then
            Dim bg As BindingGroup = f.BindingGroup
            If bg IsNot Nothing Then
                For Each be As BindingExpression In bg.BindingExpressions.OfType(Of BindingExpression)()
                    Dim propertyName = be.ParentBinding.Path.Path
                    Dim propertyInfo As System.Reflection.PropertyInfo = ReferenceType.GetProperty(propertyName)
                    Select Case propertyInfo.PropertyType
                        Case GetType(DateTime)
                            be.ParentBinding.ValidationRules.Add(New DateValidationRule(Me.DateMessage))
                        Case GetType(Int16)
                            be.ParentBinding.ValidationRules.Add(New IntegerValidationRule(Int16.MinValue, Int16.MaxValue, Me.IntegerMessage))
                        Case GetType(Int32)
                            be.ParentBinding.ValidationRules.Add(New IntegerValidationRule(Int32.MinValue, Int32.MaxValue,Me.IntegerMessage))
                        Case GetType(Int64)
                            be.ParentBinding.ValidationRules.Add(New IntegerValidationRule(Int64.MinValue, Int64.MaxValue, Me.IntegerMessage))
                        Case GetType(Single)
                            be.ParentBinding.ValidationRules.Add(New NumericValidationRule(Single.MinValue, Single.MaxValue,Me.NumericMessage))
                        Case GetType(Double)
                            be.ParentBinding.ValidationRules.Add(New NumericValidationRule(Double.MinValue, Double.MaxValue, Me.NumericMessage))
                        Case GetType(Decimal)
                            be.ParentBinding.ValidationRules.Add(New NumericValidationRule(Decimal.MinValue, Decimal.MaxValue, Me.NumericMessage))
                        Case GetType(Integer)
                            be.ParentBinding.ValidationRules.Add(New IntegerValidationRule(Integer.MinValue, Integer.MaxValue, Me.IntegerMessage))
                    End Select
                    If Setters.Contains(propertyName) Then
                        For Each v As System.Windows.Controls.ValidationRule In Setters.Item(propertyName).ValidationRules
                            be.ParentBinding.ValidationRules.Add(v)
                        Next
                    End If
                Next
            End If
        End If
    End If
End Sub

 

This method will be called during the Initialized event, when Bindings have been established but still can be modified. It inspects the type of the property and adds one of a choice of default ValidationRules, which basically make a difference only between date, integer and other numerical values, providing different value ranges as feedback to the end user. So, the class translates from the technical, physical representation, which is determined by the CLR type of the property, to the logical limitations that type entails, which is what is relevant for the user.

So, although there are different checks for different allowed value ranges, there are only three configurable, i.e. localizable, validation messages:

Public Property DateMessage As String = "Must be a date value."

Public Property IntegerMessage As String = "Must be an integer between {1} and {2}."

Public Property NumericMessage As String = "Must be a number between {1} and {2}."

 

The default ValidationRules use basic .Net functions in order to determine whether .Net can convert the value:

Protected Overrides Function ValidateCore(ByVal value As Object, ByVal cultureInfo As System.Globalization.CultureInfo) As Boolean
    Dim result As Boolean = IsNumeric(value)
    If result And MinValue.HasValue Then
        result = CDbl(value) >= MinValue
    End If
    If result And MaxValue.HasValue Then
        result = CDbl(value) <= MaxValue
    End If
    Return result
End Function

 

Finally, the allowed minimum and maximum values are formatted into the validation message, to give the users complete feedback on what they have to enter in order to make it a valid value:

Protected Overrides Function FormatMessage(ByVal value As Object) As String
    Return String.Format(Me.Message, value, MinValue, MaxValue)
End Function

 

Conclusion

The TypeValidationRuleSetter class allows it to include meaningful and localizable validation messages for type checks with minimal effort. At the same time, it protects the application from exceptions which would otherwise be thrown because of type mismatches between what is entered into a TextBox and the type of the bound property.

Using Validation.ErrorTemplate

December 16, 2009 8 comments

This was originally published as a thread in the MSDN WPF forum before I started this blog. However, today I revisited the thread and decided to maintain it here, now..

There are a couple of known problems and unknown features with Validation.ErrorTemplate that came up frequently, and I’d like to collect the workarounds for these things in one place. This is how far I got:

Problem: The Content of the ErrorTemplate is not displayed correctly

e.g. the red border doesn’t fit around the control, or parts of the error template are not drawn. The reason is that there may not be enough space around the control to display the error template, so the solution is to make the margin of the control big enough that the error template can be drawn.

Unknown Feature: The DataContext of the ErrorTemplate is the Validation.Errors Collection

Haven’t found this in the documentation, but it means that the easiest way to get at an error message in an error template is to use {Binding Path=/ErrorContent}.

Problem: Have to Change the Style of a Control in Order to Set the Error Message as ToolTip

It seems to be impossible to set the ToolTip of a control using Styles or Triggers in the error template. Usually this is worked around by modifying the Style of the control, as in the example from the WPF SDK. However, this has the disadvantage that one has to change the style of the control in order to accommodate Validation, which might interfere with other uses of the Style. There are better workarounds: most elegant by using a ToolTip on some error indicator that is part of the error template, or, if it must be the control itself, using a custom attached property on the AdornedElementPlaceholder like this:

Public Shared ReadOnly AdornedElementToolTipProperty As DependencyProperty = DependencyProperty.RegisterAttached("AdornedElementToolTip", GetType(Object), GetType(Validation), New PropertyMetadata(Nothing, AddressOf OnAdornedElementToolTipChanged))
Public Shared Function GetAdornedElementToolTip(ByVal d As DependencyObject) As Object
    Return d.GetValue(AdornedElementToolTipProperty)
End Function
Public Shared Sub SetAdornedElementToolTip(ByVal d As DependencyObject, ByVal value As Object)
    d.SetValue(AdornedElementToolTipProperty, value)
End Sub
Private Shared Sub OnAdornedElementToolTipChanged(ByVal d As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)
    Dim placeholder As AdornedElementPlaceholder = TryCast(d, AdornedElementPlaceholder)
    If placeholder IsNot Nothing Then
        Dim control As Control = TryCast(placeholder.AdornedElement, Control)
        If control IsNot Nothing Then
            control.ToolTip = e.NewValue
        End If
    End If
End Sub

One can bind the error message to this property, and this way the error template works without interfering with the control’s style.

Problem: The Error Template is not Displayed when a Page is Loaded

This is by design, since one could assume that the user doesn’t want to see error messages before he/she made any mistakes, but sometimes one needs this functionality. So, the ValidatesOnTargetUpdated property was introduced on the ValidationRule class; by setting it to true, one sees the validation result immediately.

However, there is one caveat: you must make sure that you set the DataContext after the page is initialized; this would be either in the constructor after the generated comment line that says that initialization code should go there, or in the Loaded event. If you want to set the DataContext in XAML, you find a solution for this problem here (among other things): https://wpfglue.wordpress.com/2009/12/08/navigating-from-object-to-object/

Anyway, I hear this will be fixed in WPF 4.0.

Problem: When Using an Expander, the Error Template Remains Visible even if the Expander is Collapsed

This problem is a known bug, unfortunately as far as I know not yet scheduled for fixing. The workaround (found in the MSDN WPF forum) is binding the Visibility property of the outermost element of the error template to the AdornedElement.IsVisible property of the AdornedElementPlaceholder, using the BooleanToVisibilityConverter that comes with WPF.

Standard Error Template

So, this would be my standard error template:

<ControlTemplate x:Key="errorTemplate">
    <Border BorderBrush="Red" BorderThickness="1">
        <Border.Visibility>
            <Binding ElementName="placeholder" Path="AdornedElement.IsVisible">
                <Binding.Converter>
                    <BooleanToVisibilityConverter/>
                </Binding.Converter>
            </Binding>
        </Border.Visibility>
        <StackPanel>
            <AdornedElementPlaceholder x:Name="placeholder"
             v:Validation.AdornedElementToolTip="{Binding Path=/ErrorContent}"/>
        </StackPanel>
    </Border>
</ControlTemplate>

And this would be how I use it:

<StackPanel>
    <TextBox x:Name="Abox" Margin="3" Validation.ErrorTemplate="{StaticResource errorTemplate}"
      Text="{Binding Path=A, UpdateSourceTrigger=PropertyChanged,
      ValidatesOnDataErrors=True}" />
    <Expander Header="Test" IsExpanded="True" Validation.ErrorTemplate="{x:Null}">
        <Expander.BindingGroup>
            <BindingGroup/>
        </Expander.BindingGroup>
        <TextBox x:Name="Bbox" Margin="3"
         Validation.ErrorTemplate="{StaticResource errorTemplate}"
         Text="{Binding Path=B, UpdateSourceTrigger=PropertyChanged,
          ValidatesOnDataErrors=True}"/>
    </Expander>
</StackPanel>

Categories: Validation Tags: , ,

Forwarding the Result of WPF Validation in MVVM

December 3, 2009 2 comments

A question that is frequently raised in connection with MVVM design is how one can access the result of WPF Validation (Validation.HasError) from the ViewModel. There is a way, but before I start explaining it, I’d like to ask an important question:

Why Would You Want to Do it?

Normally you would say: actually the ViewModel is responsible for validating the user input. So, if the input is invalid, the ViewModel should already know about it, isn’t it? Well, partially. There are some cases were the user input never reaches the ViewModel: when what the user entered cannot be converted to the correct data type, e.g. with date values or numbers. Catching these conversion errors in the ViewModel requires that you wrap each and every property of the data model that has one of these data types into a ViewModel property of type string, which bloats the ViewModel unnecessarily, and creates exactly the kind of glue code one tries to avoid by using the MVVM pattern in the first place.

One possibility to work around this is to create general, reusable ValidationRules for type checking and assign them to the Bindings. But then the ViewModel would have to know about the results of these checks in order to disable or enable commands or make other decisions based on the validity of its data. This would be the use case for the following technique:

How to Do it

The trick is to create a Sticky Property (which is a special case of an attached property) which, different from Validation.HasError, allows data bindings with direction to the source:

Public Shared ReadOnly MVVMHasErrorProperty As DependencyProperty = _
    DependencyProperty.RegisterAttached("MVVMHasError", GetType(Boolean), GetType(Window1), _
    New FrameworkPropertyMetadata(False, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, _
    Nothing, AddressOf CoerceMVVMHasError))
Public Shared Function GetMVVMHasError(ByVal d As DependencyObject) As Boolean
    Return d.GetValue(MVVMHasErrorProperty)
End Function
Public Shared Sub SetMVVMHasError(ByVal d As DependencyObject, ByVal value As Boolean)
    d.SetValue(MVVMHasErrorProperty, value)
End Sub

 

In the metadata of this property, I set a flag that tells WPF that this property expects to forward its value to the source, so we don’t have to set the Binding Mode on the bindings in XAML.

In order to reflect the value of Validation.HasError, we need to set up an event that fires whenever the value of this property changes. In order to find out that we need this event, the CoerceValue callback of the property checks whether the property is data bound:

Private Shared Function CoerceMVVMHasError(ByVal d As DependencyObject, ByVal baseValue As Object) As Object
    Dim result As Boolean = baseValue
    If BindingOperations.IsDataBound(d, MVVMHasErrorProperty) Then
        If GetHasErrorDescriptor(d) Is Nothing Then
            Dim desc As System.ComponentModel.DependencyPropertyDescriptor = System.ComponentModel.DependencyPropertyDescriptor.FromProperty(System.Windows.Controls.Validation.HasErrorProperty, d.GetType)
            desc.AddValueChanged(d, AddressOf OnHasErrorChanged)
            SetHasErrorDescriptor(d, desc)
            result = System.Windows.Controls.Validation.GetHasError(d)
        End If
    Else
        If GetHasErrorDescriptor(d) IsNot Nothing Then
            Dim desc As System.ComponentModel.DependencyPropertyDescriptor = GetHasErrorDescriptor(d)
            desc.RemoveValueChanged(d, AddressOf OnHasErrorChanged)
            SetHasErrorDescriptor(d, Nothing)
        End If
    End If
    Return result
End Function
Private Shared Sub OnHasErrorChanged(ByVal sender As Object, ByVal e As EventArgs)
    Dim d As DependencyObject = TryCast(sender, DependencyObject)
    If d IsNot Nothing Then
        d.SetValue(MVVMHasErrorProperty, d.GetValue(Validation.HasErrorProperty))
    End If
End Sub

Private Shared ReadOnly HasErrorDescriptorProperty As DependencyProperty = DependencyProperty.RegisterAttached("HasErrorDescriptor", GetType(System.ComponentModel.DependencyPropertyDescriptor), GetType(Window1))
Private Shared Function GetHasErrorDescriptor(ByVal d As DependencyObject) As System.ComponentModel.DependencyPropertyDescriptor
    Return d.GetValue(HasErrorDescriptorProperty)
End Function
Private Shared Sub SetHasErrorDescriptor(ByVal d As DependencyObject, ByVal value As System.ComponentModel.DependencyPropertyDescriptor)
    d.SetValue(HasErrorDescriptorProperty, value)
End Sub

And that’s it… Notice that the CoerceValue callback uses a private DependencyProperty in order to store the DependencyPropertyDescriptor that is used to set up the change event. This has two purposes: it allows to remove the notification conveniently, and more important, it serves as a flag in order to see whether the notification is already in place or still needs to be set up.

You can download an example for this technique here. (You will have to change the extension to .zip in order to extract the contents).

Credits

I found the information about how to monitor changes in any object’s DependencyProperties in this article by Charles Petzold.

Categories: Validation Tags: ,

Submit Sticky Command Example

November 21, 2009 Leave a comment

WPFGluePublished.zip on the Downloads page contains an example project called SubmitCommandExample which demonstrates how to use a Sticky Command in order to submit validated user input from a group of data bound controls to the object they bind to. The command will only be enabled if the input passes validation, and invalid data will not be forwarded to the data object.

WPFGlue Categories

November 7, 2009 Leave a comment

I’d like to classify WPFGlue components according to what they do in the application. So far, the following categories came out:

Navigation

The WPF navigation framework is just great. Especially the journal: you get the complete forwards and backwards navigation for free, the journal remembers the user input on the page, and this works even when pages are freed in order to save memory.

However, WPF navigation stops just short of being really sticky. Selecting an object on one page and passing it forward to another page is supposed to be done through code behind, and the journal serializes objects instead of hold references to them, which is in itself a wise thing to do in order to not keep these objects from being freed, but makes it difficult to return to the same object instance if one goes back to a page. Also, I don’t like the suggestion in the WPF documentation that the navigation topology should be defined through pages referencing each other through hyperlinks. In my opinion, the pages should work stand-alone as much as possible, and there should be a central definition for the navigation topology, defining the structure of the application and mapping pages to the appropriate places in the object model.

WPFGlue supports navigation through a sticky GoToPage CommandBinding which takes an object parameter and passes it to the DataContext of the new page, a sticky Session property which can hold WeakReferences to business objects while the journal stores pointers to these references, and a ViewModelKit for defining a navigation menu.

Localization

The need for localization is immediately obvious for every programmer whose mother tongue is not English. Surprisingly, in WPF localization seems to have been added more or less as an afterthought. While Visual Studio supports localization very nicely for Windows Forms applications, getting at resources contained in satellite assemblies is not straight forward in WPF, and even something as basic as detecting the system language and making it the default culture for display and formatting in the application requires extra work. I could imagine that in the beginning the creators of WPF thought that people would just write different XAML pages for different languages, as is done in HTML, and that the need for localizing XAML only arose after the power of XAML as a programming language became more apparent, and programming elements became more predominant on XAML pages than clear text.

In the Localization category, WPFGlue comes with some sticky MarkupExtensions which make it easier to localize an application. These MarkupExtensions get their values from standard .Net localized resources, so no need for LocBAML or third party tools there. Other MarkupExtensions can be used to set WPF elements’ Language property to the CurrentCulture or CurrentUICulture.

Commanding

The commanding support in WPF is ingenious. It allows to implement some functionality once and to attach it to all the places where it is applicable, through RoutedCommands and CommandBindings. At the same time, commands decouple the traditional menu and toolbar from the application’s object model, making for great reusable code. Fabulously sticky stuff!

Commands are also the way ViewModels in the MVVM pattern expose their functionality to the user interface. The only unsticky thing about them is that CommandBindings support adding handler routines only through code, either code behind or explicitly. Also, there is no native general way of wrapping a method call into a command.

In the Commanding category, WPFGlue should support mapping commands to method calls, and also direct commands (ICommand implementations that are exposed by a ViewModel) to routed commands which can be handled centrally. There should be a way to set up a CommandSet and to connect it to a page, thus being able to define the available commands and their handlers in a central location. Also, the Commanding category contains a CommandGroup, which executes a series of commands in one go.

Validation

MVVM developers seem to avoid the validation features of WPF in favour of validation through the ViewModel or business model, exposed through the IDataErrorInfo interface. I don’t really understand why.

First of all, providing error messages to a user is not the job of a business object. The business object shouldn’t know whether it is used by an interactive user, a background service, or a batch job. In addition to that, in order to be able to provide meaningful error messages, the business object model would have to localize its error messages, and I wouldn’t want to burden my business objects with a job like that.

Second, while the business model knows what itself can do, it has no idea about what the developer of an application that reuses it may want to do. It would be perfectly justified to forbid certain legal property values because they are outside the scope of the current application. In this case, the ViewModel would have to constrain the values it allows for a certain property before it passes them on to the business object model.

So why not let the ViewModel do the job? I agree, the ViewModel is responsible. However, I wouldn’t use IDataErrorInfo, because that forces me to re-implement a lot of functionality that is already present in the WPF validation model. For example, resetting the values of an object to their pre-edit state before they are committed while still being able to provide instant validation feedback is really easy with ValidationRules and a BindingGroup, but requires a lot of effort, including ongoing maintenance effort, when done in a ViewModel specialized for a certain business model.

So, what I’d do instead is create a ViewModelKit that allows one to define a group of ValidationRules, which may or may not be dependent on validation functions in the business object model, and to allow setting these ValidationRules to a BindingGroup using a sticky property.

In addition to that, one would find workarounds for some minor problems with Validation.ErrorTemplate and ways to inform the ViewModel of validation results in the Validation category of WPFGlue.

Application Services

Application services would be things like forwarding application lifetime events to the ViewModel or providing services that are available in the operating system environment without creating a dependency from the ViewModel to the View.

Examples for this are a MessageBox that is invoked through sticky properties, a sticky StartupCommand property, a markup extension that allows to include application settings in the user interface, or context sensitive help through sticky properties.

Common Tasks

Common tasks would be jobs that recur in user interfaces, so that it makes sense to implement them in sticky ViewModels.

Examples would be a FileManager that provides support for the usual File menu commands for any type of document, a ListEditor that has commands for moving, adding and deleting items in collections implementing the standard collection interfaces, a ListPager that divides a collection into pages of a given length, and the like.

Conclusion

This is only a tentative list. The only category that will definitely survive is Common Tasks, since the others hopefully will be replaced by native support through WPF as the framework develops and the gaps between business object model and user interface vanish, so that there is no more need to fill them with glue.