Data Validation
The data validation mechanism allows you to check cell values and show errors in cells that contain invalid data.
The TreeList and TreeView controls perform data validation when a user modifies a cell value and then tries to save (post) the value. The controls also invoke the validation mechanism for newly displayed cells, and cells updated in code even if a user has not modified cell values.
Validate Values when User Modifies Cell Data
The TreeList and TreeView controls activate the cell value validation mechanism when a user modifies a cell value and tries to save it in the data source (for instance, a user presses the ENTER key or moves focus to another cell). The following diagram shows the validation mechanism stages performed when a user modifies a cell value:
A cell's in-place editor performs initial value validation during data input. For instance, a
SpinEditor
, which accepts only numeric values, shows errors if a user tries to type a letter.A user cannot leave a cell until a valid value is entered, or the ESC key is pressed, which reverts to the previous value.
The TreeList/TreeView control checks whether DataAnnotations attributes are applied to the data source's business object, and then validates data according to these rules. This validation stage is enabled if the
DataControlBase.ShowItemsSourceErrors
property is set totrue
(the default value).A user cannot leave a cell until a valid value is entered, or the ESC key is pressed, which reverts to the previous value.
The control fires the
DataControlBase.ValidateCellValue
event, which you can handle to implement custom value validation.A user cannot leave a cell until a valid value is entered, or the ESC key is pressed, which reverts to the previous value.
The control posts the cell value. At this step, the data source may raise exceptions if the posted data is invalid. The control shows an error in the cell if any exception is caught.
A user cannot leave a cell until a valid value is entered, or the ESC key is pressed, which reverts to the previous value.
The TreeList/TreeView control checks whether the business object implements the IDataErrorInfo or INotifyDataErrorInfo interface, and uses these interfaces to fetch cell errors, if any. This validation stage is enabled if the
DataControlBase.ShowItemsSourceErrors
property is set totrue
(the default value).The control allows a user to leave a cell when the cell value is invalid, because the value has already been posted.
Validate Values of Newly Displayed Cells and Cells Updated in Code
The controls support the validation mechanism for newly displayed and updated cells even if a user has not modified cell values. The control validates cell values in the following cases:
- The control is initially displayed, and so cells are drawn within the viewport.
- A cell becomes visible when a control is scrolled.
- A cell value is modified in code, and so the control needs to redraw the cell.
The following diagram shows the validation mechanism stages in these scenarios:
The TreeList/TreeView control checks whether DataAnnotations attributes are applied to the data source's business object, and then validates data according to these rules. This validation stage is enabled if the
DataControlBase.ShowItemsSourceErrors
property istrue
(the default value).The TreeList/TreeView control checks whether the business object implements the IDataErrorInfo or INotifyDataErrorInfo interface, and uses these interfaces to fetch cell errors, if any. This validation stage is enabled if the
DataControlBase.ShowItemsSourceErrors
property is set totrue
(the default value).The control fires the
DataControlBase.ValidateCellValue
event, which you can handle to implement custom value validation. This validation stage is enabled if theDataControlBase.ValidateCellValuesOnShowAndUpdate
property istrue
(the default value isfalse
).
Data Source Validation Rules
If the DataControlBase.ShowItemsSourceErrors
property is enabled (the default behavior), the TreeList/TreeView control validates data using the DataAnnotations
attributes and IDataErrorInfo
interface applied to the data source's business object.
DataAnnotations Attributes
You can apply DataAnnotations
validation attributes (System.ComponentModel.DataAnnotations.ValidationAttribute
descendants) to a business object to specify validation rules for the object's properties. The list below shows most common validation attributes:
CompareAttribute
— Provides an attribute that compares two properties.CustomValidationAttribute
— Specifies a custom validation method that is used to validate a property or class instance.MaxLengthAttribute
— Specifies the maximum length of array or string data allowed in a property.MinLengthAttribute
— Specifies the minimum length of array or string data allowed in a property.RangeAttribute
— Specifies the numeric range constraints for the value of a data field.RegularExpressionAttribute
— Specifies that a data field value must match the specified regular expression.RequiredAttribute
— Specifies that a data field value is required.StringLengthAttribute
— Specifies the minimum and maximum length of characters that are allowed in a data field.
Example
The following code applies a StringLength
attribute to a business object's Code property. The attribute forces a user to enter a string containing at least 4, but no more than 8 characters.
public partial class Department : ObservableObject, IDataErrorInfo
{
//...
[ObservableProperty]
[property: StringLength(8, MinimumLength = 4)]
public string code = "0000";
}
'IDataErrorInfo' Interface
You can implement the System.ComponentModel.IDataErrorInfo
interface for a business object to specify validation rules for the business object's properties.
Currently, the TreeList and TreeView controls only support errors that are set for individual cells, not for entire rows. Thus, the IDataErrorInfo.Item[String]
property is only in effect, and the IDataErrorInfo.Error
property is ignored.
Example - Implement 'IDataErrorInfo' interface
The following example implements the IDataErrorInfo
interface for a business object to return an error if the Phone property is empty.
public partial class Department : ObservableObject, IDataErrorInfo
{
//...
[ObservableProperty]
public string phone = "0";
string IDataErrorInfo.this[string columnName]
{
get
{
if (columnName != "Phone")
return "";
return string.IsNullOrEmpty(this.Phone) ? "Please specify the phone number" : "";
}
}
string IDataErrorInfo.Error
{
get { return ""; }
}
}
'INotifyDataErrorInfo' Interface
The System.ComponentModel.INotifyDataErrorInfo
interface allows you to implement custom validation rules at the business object level. The interface supports synchronous and asynchronous validation, multiple errors per property, and cross-property errors.
Example - Implement 'INotifyDataErrorInfo' interface
The following example demonstrates how to implement the INotifyDataErrorInfo
interface for a business object (ProjectTask). The interface is used to define validation rules for the ProjectTask.EstimateTime property. When the property's value violates the validation rules, the TreeList control displays an error in a corresponding cell.
The INotifyDataErrorInfo
interface implementation shown below supports multiple errors per property at the same time.
<mxtl:TreeListControl x:Name="treeList" ItemsSource="{Binding Tasks}"...>
<mxtl:TreeListColumn FieldName="EstimateTime" Header="Estimate Time (h)" Width="*" />
</mxtl:TreeListControl>
public partial class TreeListDataEditorsPageViewModel : PageViewModelBase
{
//...
public List<ProjectTask> Tasks { get; }
}
public partial class ProjectTask : ObservableObject, INotifyDataErrorInfo
{
public ProjectTask(ProjectTask parent, string description, TaskStatus status,
int estimateTime, int timeSpent, string assignee, DateTime dueDate)
{
Tasks = new();
Parent = parent;
this.estimateTime = estimateTime;
//...
}
[ObservableProperty]
private int estimateTime;
private Dictionary<string, List<string>> propertyErrors = new Dictionary<string, List<string>>();
partial void OnEstimateTimeChanged(int oldValue, int newValue) => ValidateEstimateTime();
public bool HasErrors => propertyErrors.Any();
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
public System.Collections.IEnumerable GetErrors(string propertyName)
{
if (propertyErrors.ContainsKey(propertyName))
return propertyErrors[propertyName];
else
return null;
}
private void RaiseErrorsChanged(string propertyName)
{
ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName));
}
private void ValidateEstimateTime()
{
ClearErrors(nameof(EstimateTime));
if (EstimateTime < 1)
AddError(nameof(EstimateTime), "The Estimate Time cannot be less than 1.");
if (EstimateTime > 800)
AddError(nameof(EstimateTime), "The Estimate Time cannot be greater than 800");
}
private void AddError(string propertyName, string error)
{
if (!propertyErrors.ContainsKey(propertyName))
propertyErrors[propertyName] = new List<string>();
if (!propertyErrors[propertyName].Contains(error))
{
propertyErrors[propertyName].Add(error);
RaiseErrorsChanged(propertyName);
}
}
private void ClearErrors(string propertyName)
{
if (propertyErrors.ContainsKey(propertyName))
{
propertyErrors.Remove(propertyName);
RaiseErrorsChanged(propertyName);
}
}
public ProjectTask Parent { get; }
public List<ProjectTask> Tasks { get; }
//...
}
Control's 'ValidateCellValue' Event
The DataControlBase.ValidateCellValue
event allows you to implement custom value validation logic.
Raising the DataControlBase.ValidateCellValue
event is a non-optional stage of the validation mechanism invoked after a user has modified a cell value.
The validation mechanism is also used to check the validity of newly displayed cells and cells updated in code. In this case, the DataControlBase.ValidateCellValue
event only fires if the inherited DataControlBase.ValidateCellValuesOnShowAndUpdate
property is true
(the default value is false
).
Example
The following example handles the ValidateCellValue
event to show errors when the Date1 property's value is greater than the Date2 property's value.
public partial class Department : ObservableObject, IDataErrorInfo
{
[ObservableProperty]
public DateTime date1 = new DateTime();
[ObservableProperty]
public DateTime date2 = new DateTime();
}
treeList1.ValidateCellValue += TreeList1_ValidateCellValue;
// Uncomment the following code line to use the ValidateCellValue event handler
// to check cells when they are displayed or modified in code:
// treeList1.ValidateCellValuesOnShowAndUpdate = true;
private void TreeList1_ValidateCellValue(object? sender, TreeListValidateCellValueEventArgs e)
{
Department dep = e.Node.Content as Department;
if(e.Column.FieldName == "Date1")
{
DateTime value1 = Convert.ToDateTime(e.Value);
DateTime value2 = dep.Date2;
if(value1 > value2)
{
e.ErrorContent = "Date1 must be less than Date2";
return;
}
}
}