Friday, May 30, 2014

Prevent System.Windows.Data Error in Output window while evaluating Validation.Errors in XAML


Validation of UI input parameters in WPF is a frequently requirement. There are several approaches to realize an input validation. You can use custom ValidationRule, ExceptionValidationRule or IDataErrorInfo. Excellent descriptions how to use validation in WPF can be found here:
If validation fails, it is desirable to show an error message with a hint in the GUI. In the MSDN library Microsoft shows an example of how to implement this (http://msdn.microsoft.com/en-us/library/system.windows.data.binding.validationrules%28v=vs.110%29.aspx).:
<Style x:Key="textBoxInError" TargetType="{x:Type TextBox}">
 <Style.Triggers>
  <Trigger Property="Validation.HasError" Value="true">
   <Setter Property="ToolTip"
           Value="{Binding RelativeSource={x:Static RelativeSource.Self},
                           Path=(Validation.Errors)[0].ErrorContent}" />
  </Trigger>
 </Style.Triggers>
</Style>
This works fine, but produces some exceptions in the Output window, if the error is corrected after occurring:
System.Windows.Data Error: 17 :
Cannot get 'Item[]' value (type 'ValidationError') from '(Validation.Errors)'
(type 'ReadOnlyObservableCollection`1').
BindingExpression:Path=(0)[0].ErrorContent; DataItem='TextBox'
(Name='Input1'); target element is 'TextBox'
(Name='Input1'); target property is 'ToolTip' (type 'Object')
ArgumentOutOfRangeException:'System.ArgumentOutOfRangeException: Specified
argument was out of the range of valid values.
Parameter name: index'
The Attached Property Validation.Errors of the Validation class has no elements, accessing the first element results in an ArgumentOutOfRangeException. System.Collections.ObjectModel.ReadOnlyObservableCollection<ValidationError> is the type of the Validation.Errors Attached Property. This type is in XAML wrapped by CollectionView. Instead of using the index of the ReadOnlyObservableCollection that lead to an exception, if there are no errors, we can use the CurremtItem property of the CollectionView. The property will return nothing if there are no errors, so no exception will be produced:
<Setter Property="ToolTip"
        Value="{Binding RelativeSource={x:Static RelativeSource.Self},
                 Path=(Validation.Errors).CurrentItem.ErrorContent}"/>

Saturday, May 24, 2014

Enumeration in WPF/XAML


Enumeration types  can be set by the keyword enum. Often it is needed to view all values of the enumeration. The following example shows how this can be done by setting all values to a ComboBox, so that it can be selected for further actions.

First I have defined an enum.

public enum State
{
    Unknown,
    Idle,
    WaitingForInput, 
    NoDisplayState
}

To show all Enum values in a ComboBox I have set the namespace of mscorlib to get Enum and the namespace of the local assembly to get then enum State.

xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:EnumExample"

Then I have defined an ObjectDataProvider to get all State values.

    <Window.Resources>
        <ObjectDataProvider x:Key="StateValues"
                        ObjectType="{x:Type sys:Enum}"
                        MethodName="GetValues">
            <ObjectDataProvider.MethodParameters>
                <x:Type TypeName="local:State" />
            </ObjectDataProvider.MethodParameters>
        </ObjectDataProvider>
    </Window.Resources>

The ObjectDataProvider is referenced by the defined x:Key in the ItemsSource of the ComboBox.

        <ComboBox ItemsSource="{Binding Source={StaticResource StateValues}}"
                  SelectedItem="{Binding SelectedStateValue}"/>

Instead of setting an ObjectDataProvider in the XAML View, it is possible to set an Array in code-behind or in the ViewModel.

StateEnums = Enum.GetNames(typeof(State));

public Array StateEnums
{
    get;
    set;
}

The Array is then binded to the ItemsSource of the ComboBox.

        <ComboBox ItemsSource="{Binding StateEnums}"
                  SelectedItem="{Binding SelectedStateEnum}" />