Introduction
This is something that I created for another developer when he was having issues with validation. Normally, one does not have integer values assigned to enumerations, and when they are assigned, there is an enumeration that is assigned the value zero. The RequiredAttibute
does not recognize that 0
may not be a valid enumeration, so does not flag it as an error. Normally, if you encounter this problem, enumerations would be used in a ComboBox
, and this would only be a problem for a new record where a value has not yet been assigned to the ComboBox
, so the default of “0
” is used.
Solution
using System;
using System.ComponentModel.DataAnnotations;
public class RequiredEnumAttribute : RequiredAttribute
{
public override bool IsValid(object value)
{
if (value == null) return false;
var type = value.GetType();
return type.IsEnum && Enum.IsDefined(type, value);;
}
}
This is a very similar to the RequiredAttibute
where the IsValid
method is overridden. Initially, just check that the new value is not null
, and then check if its value is an enumeration, and that the value is defined.
The Sample
The sample has a number of ComboBox
es that have their ItemsSource
bound to a property that returns an enumeration of all the enumerations of a specific enumeration definition. The SelectedItem
is bound to a separate property that is the type of the enumeration, or a Nullable
of the enumeration. There are two properties for binding to the ItemsSource
, one for an enumeration that does not have numeric assignments, and one that does. The one that has numeric assignments does not have an enumeration assigned to zero. As can be seen from the sample, the enumeration defaults to the integer value of zero even if a zero value is not assigned to the enumeration. A zero assignment will happen automatically if there are no assignment. Making an enumeration property Nullable
will mean that there is no default value assigned to an enumeration, and the RequiredAttribute
will force the user to select a value to continue. Manually assigning enumeration values and not assigning an enumeration for zero, and using the RequiredEnumAttribute
can accomplish the same result.
If you investigate the XAML, you will see that the binding definition has a “ValidatesOnDataErrors=True
” which is what tells the binding that it needs to check for errors using the IDataErrorInfo
interface:
<ComboBox Grid.Row="4"
Grid.Column="1"
Margin="5"
VerticalAlignment="Center"
ItemsSource="{Binding TypeBs}"
SelectedItem="{Binding TypeB3,
ValidatesOnDataErrors=True}" />
I have often forgotten about needing this attribute, and I know that I have found this error when working with other developers.
The decoration for using this validation attribute is no different from any other validation attribute:
[RequiredEnum(ErrorMessage = "TypeB3 is required.")]
public TypeB TypeB3 { get; set; }
I have obviously significantly simplified the property since normally would raise the PropertyChanged
event in a real application. All the arguments that can be used with the Required
decoration can be used with the RequiredEnum
because it is derived from the class.
Bonus
Because I have to implement IDataErrorInfo
in the ViewModel
to provide error validation, I have a class called ValidationViewModelBase
class. It is used by the ViewModel
to implement the interface
required for the IDataErrorInfo
. Since C# does not have multiple inheritance (which I think is a big mistake, and this is a good example of where multiple interface), if you use this class, you will probably want to include the INotifyPropertyChanged
interface. However, you can easily adapt this code for your use, and thus easily provide the IDataErrorInfo
interface code required when validation attributes are used.
History
- 12/05/2015 Initial version