WPF Editable DataGrid and Validation: A Comprehensive Guide
Windows Presentation Foundation (WPF) provides a robust framework for building rich, interactive user interfaces, and one of its key features is the DataGrid control. The DataGrid control in WPF is highly versatile and can be enhanced to support editing functionalities alongside data validation. Here, we will delve into how to create an editable DataGrid with robust validation mechanisms in WPF.
Setting Up the DataGrid
To create an editable DataGrid, ensure your WPF project references the WindowsBase
, PresentationCore
, and PresentationFramework
assemblies.
Step 1: Add a DataGrid to Your XAML
To start, define a DataGrid control in your XAML file. Let's create an example with a simple User table that includes fields like FirstName, LastName, and Age.
<Window x:Class="WpfApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="450" Width="750">
<Grid>
<DataGrid x:Name="dgUsers" AutoGenerateColumns="False" IsSynchronizedWithCurrentItem="True" CanUserAddRows="True" Margin="10">
<DataGrid.Columns>
<DataGridTextColumn Header="First Name" Binding="{Binding FirstName, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" Width="*"/>
<DataGridTextColumn Header="Last Name" Binding="{Binding LastName, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" Width="*"/>
<DataGridTextColumn Header="Age" Binding="{Binding Age, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" Width="*"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
Here, AutoGenerateColumns
is set to False
to manually create the columns, and IsSynchronizedWithCurrentItem
helps to keep the DataGrid's current item in sync with the UI. CanUserAddRows
is set to True
to allow users to add new rows.
Step 2: Define a Collection of Objects
In the MainWindow.xaml.cs
file, create a collection of objects to bind to the DataGrid.
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
namespace WpfApp
{
public partial class MainWindow : Window
{
public ObservableCollection<User> Users { get; set; }
public MainWindow()
{
InitializeComponent();
Users = new ObservableCollection<User>
{
new User { FirstName = "John", LastName = "Doe", Age = 30 },
new User { FirstName = "Jane", LastName = "Smith", Age = 25 }
};
dgUsers.ItemsSource = Users;
}
}
public class User : INotifyPropertyChanged, IDataErrorInfo
{
private string firstName;
private string lastName;
private int age;
public string FirstName
{
get => firstName;
set
{
if (firstName != value)
{
firstName = value;
OnPropertyChanged();
}
}
}
public string LastName
{
get => lastName;
set
{
if (lastName != value)
{
lastName = value;
OnPropertyChanged();
}
}
}
public int Age
{
get => age;
set
{
if (age != value)
{
age = value;
OnPropertyChanged();
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
public string Error => null;
public string this[string columnName]
{
get
{
string result = null;
switch (columnName)
{
case nameof(FirstName):
result = !string.IsNullOrEmpty(FirstName) ? null : "First name is required.";
break;
case nameof(LastName):
result = !string.IsNullOrEmpty(LastName) ? null : "Last name is required.";
break;
case nameof(Age):
result = Age > 0 ? null : "Age must be greater than zero.";
break;
}
return result;
}
}
}
}
Data Binding and Editing
In the code above, the User
class implements INotifyPropertyChanged
to support data binding and IDataErrorInfo
for validation. The ObservableCollection<User>
is used to hold the data items and update the UI as items are added or modified.
Binding and Validation
The binding in the XAML is configured with UpdateSourceTrigger=PropertyChanged
to ensure changes are pushed to the data source immediately and ValidatesOnDataErrors=True
to enable validation.
Validation Logic
The validation logic is implemented in the IDataErrorInfo
interface. The this[string columnName]
index getter checks each property against specific criteria and returns an error message if validation fails.
Adding New Rows
The CanUserAddRows
property in the DataGrid allows users to add new rows directly in the UI. When a new row is added, the bound object (in this case, User
) is instantiated and added to the ObservableCollection
.
Handling Validation Errors
When a validation error occurs, the DataGrid will typically display the error message in a tooltip when the user hovers over the cell. You can further customize this behavior by creating a custom style or template for the DataGridCell.
Custom Validation Style
For better visibility, you can apply custom styles to highlight cells with validation errors.
<Window.Resources>
<Style TargetType="DataGridCell">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={RelativeSource Self},
Path=(Validation.Errors)[0].ErrorContent}"/>
<Setter Property="Background" Value="LightPink"/>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
This style sets a custom tooltip and background color for cells with validation errors, making them more noticeable.
Summary
By leveraging WPF's强大 DataGrid control, you can easily create a user interface that supports editing and validation. In this guide, we demonstrated how to set up an editable DataGrid, bind it to a collection of objects, implement validation logic, and handle validation errors. These techniques ensure that your application provides a robust and user-friendly experience, allowing users to interact with data while maintaining data integrity.
Certainly! Below is a detailed, step-by-step guide for beginners to understand how to create an editable WPF DataGrid, implement validation, and run the application with data flow.
Step-by-Step Guide to WPF Editable DataGrid with Validation
Introduction
WPF (Windows Presentation Foundation) offers powerful data presentation features that make it easier to create complex and interactive UIs. One such feature is the DataGrid
control, which can be used to display and manipulate tabular data. In this guide, we will explore how to create an editable DataGrid
, add custom validation, and manage data bindings effectively.
Prerequisites
- Visual Studio: Make sure you have Visual Studio installed. Any edition with WPF support (Community, Professional, or Enterprise) will suffice.
- Basic Knowledge of C#, XAML, and WPF: Familiarity with these technologies will help you follow this guide more easily.
Step 1: Create a New WPF Application
- Open Visual Studio and create a new project.
- Choose "WPF App (.NET Core)" or "WPF App (.NET Framework)" depending on your preference and requirements.
- Enter a project name (e.g.,
EditableDataGridExample
) and choose a location to save it. - Click "Create".
Step 2: Design the User Interface (XAML)
In this step, we’ll design the UI to include a DataGrid
control.
- Open
MainWindow.xaml
in your project. - Modify the
Grid
element to include aDataGrid
:
<Window x:Class="EditableDataGridExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Editable DataGrid Example" Height="450" Width="800">
<Grid>
<DataGrid x:Name="MyDataGrid"
AutoGenerateColumns="False"
Margin="10"
ItemsSource="{Binding People}"
CanUserAddRows="True"
CanUserDeleteRows="True"
EnableRowVirtualization="True"
RowEditEnding="MyDataGrid_RowEditEnding">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}" MinWidth="150"/>
<DataGridTextColumn Header="Age" Binding="{Binding Age, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}" MinWidth="100"/>
<DataGridTextColumn Header="Email" Binding="{Binding Email, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}" MinWidth="150"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
Step 3: Create the Data Model
- Create a new class named
Person.cs
to represent the data model for each row in theDataGrid
:
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Runtime.CompilerServices;
namespace EditableDataGridExample
{
public class Person : INotifyDataErrorInfo, INotifyPropertyChanged
{
private string _name;
private int _age;
private string _email;
private readonly Dictionary<string, List<string>> _errorsByPropertyName = new Dictionary<string, List<string>>();
public string Name
{
get => _name;
set
{
if (_name == value) return;
_name = value;
ValidateProperty("Name", _name);
OnPropertyChanged(nameof(Name));
}
}
[Range(1, int.MaxValue, ErrorMessage = "Age must be greater than zero.")]
public int Age
{
get => _age;
set
{
if (_age == value) return;
_age = value;
ValidateProperty("Age", _age);
OnPropertyChanged(nameof(Age));
}
}
[EmailAddress(ErrorMessage = "Invalid email address.")]
public string Email
{
get => _email;
set
{
if (_email == value) return;
_email = value;
ValidateProperty("Email", _email);
OnPropertyChanged(nameof(Email));
}
}
private void ValidateProperty<T>(string propertyName, T value)
{
var results = new List<ValidationResult>();
var context = new ValidationContext(this) { MemberName = propertyName };
Validator.TryValidateProperty(value, context, results);
if (results.Any())
{
if (_errorsByPropertyName.ContainsKey(propertyName))
_errorsByPropertyName[propertyName] = results.Select(r => r.ErrorMessage).ToList();
else
_errorsByPropertyName.Add(propertyName, results.Select(r => r.ErrorMessage).ToList());
}
else
{
_errorsByPropertyName.Remove(propertyName);
}
ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName));
}
public bool HasErrors => _errorsByPropertyName.Any();
public IEnumerable GetErrors(string propertyName)
{
return _errorsByPropertyName.ContainsKey(propertyName)
? _errorsByPropertyName[propertyName]
: null;
}
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Step 4: Set Up the ViewModel
- Create a new class named
MainViewModel.cs
:
using System.Collections.ObjectModel;
namespace EditableDataGridExample
{
public class MainViewModel
{
public ObservableCollection<Person> People { get; set; }
public MainViewModel()
{
People = new ObservableCollection<Person>
{
new Person { Name = "John Doe", Age = 28, Email = "john.doe@example.com" },
new Person { Name = "Jane Smith", Age = 34, Email = "jane.smith@example.com" }
};
}
}
}
Step 5: Bind the ViewModel to the View
- Open
MainWindow.xaml.cs
and set theDataContext
to an instance ofMainViewModel
:
using System.Windows;
namespace EditableDataGridExample
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainViewModel();
}
private void MyDataGrid_RowEditEnding(object sender, System.Windows.Controls.DataGridRowEditEndingEventArgs e)
{
if (e.EditAction == System.Windows.Controls.DataGridEditAction.Commit)
{
// Here you can add code to save changes to the database or perform any post-edit actions
}
}
}
}
Step 6: Build and Run the Application
- Save All Files in your project.
- Build the Project by clicking "Build" > "Build Solution" or pressing
Ctrl+Shift+B
. - Run the Application by clicking the green "Start" button or pressing
F5
.
Step 7: Test the Application
- The application should open with a
DataGrid
displaying two pre-filled rows from thePeople
collection. - Try Editing: Click on any cell to edit the data. For instance, change the name, age, or email.
- Invalid Data: Enter invalid data (e.g., a negative age or an incorrect email format). You should see validation errors displayed in red next to the affected cells.
- Add New Rows: Click on the empty row at the bottom of the
DataGrid
to add new entries. - Delete Rows: Select a row by clicking on it and press
Delete
to remove it.
Explanation of Data Flow
Data Binding: The
DataGrid
'sItemsSource
property is bound to anObservableCollection<Person>
in theMainViewModel
. This ensures that any changes to the collection are reflected in the UI.Validation: Each property in the
Person
class includes validation attributes and implementsINotifyDataErrorsInfo
. This allows you to validate each cell's value as it changes.Editing: When you edit a cell, the
UpdateSourceTrigger=PropertyChanged
property ensures that changes are pushed back to the source immediately (i.e., thePerson
objects in the collection).Event Handling: The
RowEditEnding
event is used to perform any post-edit actions, such as saving changes to a database (though this is not implemented in this example).
By following these steps, you can create a functional WPF application with an editable DataGrid
and real-time validation. As you gain more experience, you can extend this example to include advanced features like sorting, filtering, and more sophisticated data handling.
Certainly! Below are the "Top 10 Questions and Answers" for the topic "WPF Editable DataGrid and Validation."
Top 10 Questions and Answers on WPF Editable DataGrid and Validation
1. What is a DataGrid in WPF and why is it editable?
Answer: In WPF (Windows Presentation Foundation), a DataGrid
control is used to display tabular data. An editable DataGrid
allows users to modify data directly within the grid. This is particularly useful in applications where users need to update, insert, or delete data without needing to open a separate form or window. The editable feature enhances the interactivity and user experience of data-driven applications.
2. How can I make a DataGrid editable in WPF?
Answer: To make a DataGrid
editable, set the IsReadOnly
property to false
. By default, the IsReadOnly
is set to false
, making the DataGrid
editable. You can also define DataGridTextColumn
or other appropriate columns with Binding
where UpdateSourceTrigger
can be set to PropertyChanged
or LostFocus
for immediate updates. Here is a simple example:
<DataGrid ItemsSource="{Binding MyItems}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name, UpdateSourceTrigger=PropertyChanged}" />
</DataGrid.Columns>
</DataGrid>
3. What are the different ways to implement data validation in an editable DataGrid?
Answer: Data validation in a DataGrid
can be implemented in several ways:
- INotifyDataErrorInfo interface: This provides a way to provide detailed validation error information to the UI. Each item in the
DataGrid
must implement this interface. - IDataErrorInfo interface: This is an older approach but still useful for basic validation. It allows validation logic to be defined in the model.
- Data Annotations: Using attributes from the
System.ComponentModel.DataAnnotations
namespace can be a convenient way to add validation rules directly in the model. - IValueConverter with Validation Rules: Custom
ValidationRule
classes can be added to bindings for more granular control. Here is an example using Data Annotations:
using System.ComponentModel.DataAnnotations;
public class Item
{
[Required(ErrorMessage = "Name is required")]
public string Name { get; set; }
}
4. Can you provide a complete example of validation in a DataGrid using DataAnnotations?
Answer: Sure. Here is a complete example that demonstrates how to use Data Annotations for validation in a DataGrid
:
Model (Item.cs)
using System.ComponentModel.DataAnnotations;
public class Item
{
[Required(ErrorMessage = "Name is required")]
public string Name { get; set; }
[Range(1, 100, ErrorMessage = "Age must be between 1 and 100")]
public int Age { get; set; }
}
ViewModel (MainViewModel.cs)
using System.Collections.ObjectModel;
public class MainViewModel
{
public ObservableCollection<Item> Items { get; set; } = new ObservableCollection<Item>
{
new Item { Name = "John Doe", Age = 30 },
new Item { Name = "", Age = 25 } // This will trigger Name validation
};
}
XAML (MainWindow.xaml)
<Window x:Class="WpfApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:MainViewModel />
</Window.DataContext>
<Grid>
<DataGrid ItemsSource="{Binding Items}" AutoGenerateColumns="False" Margin="10">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}" />
<DataGridTextColumn Header="Age" Binding="{Binding Age, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}" />
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
5. How can I customize the appearance of cells with validation errors in the DataGrid?
Answer: You can use a Validation.ErrorTemplate
to customize the appearance of cells with validation errors. Here's an example:
<Window.Resources>
<ControlTemplate x:Key="ValidationTemplate">
<Border BorderBrush="Red" BorderThickness="2">
<TextBlock Foreground="White" FontSize="12" Text="{Binding Path=(Validation.Errors).CurrentItem.ErrorContent, RelativeSource={RelativeSource Self}}" />
</Border>
</ControlTemplate>
</Window.Resources>
<DataGrid ItemsSource="{Binding Items}" AutoGenerateColumns="False" Margin="10">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}">
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="Validation.ErrorTemplate" Value="{StaticResource ValidationTemplate}" />
</Style>
</DataGridTextColumn.ElementStyle>
<DataGridTextColumn.EditingElementStyle>
<Style TargetType="TextBox">
<Setter Property="Validation.ErrorTemplate" Value="{StaticResource ValidationTemplate}" />
</Style>
</DataGridTextColumn.EditingElementStyle>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
6. How can I add a delete or insert functionality in an editable DataGrid?
Answer: You can implement additional functionality like insert and delete in a DataGrid
using commands. Here's a basic example for deleting items:
XAML:
<DataGrid ItemsSource="{Binding Items}" AutoGenerateColumns="False" Margin="10" CanUserDeleteRows="True" CommandBindings="{Binding CommandBindings}">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}" />
</DataGrid.Columns>
</DataGrid>
<Button Content="Add Item" Command="{Binding AddItemCommand}" Margin="10,0,10,10"/>
<Button Content="Delete Item" Command="{Binding DeleteItemCommand}" Margin="10,0,10,10"/>
ViewModel:
using System.Collections.ObjectModel;
using System.Windows.Input;
public class MainViewModel
{
private ObservableCollection<Item> _items;
public ObservableCollection<Item> Items
{
get { return _items; }
set
{
_items = value;
OnPropertyChanged(nameof(Items));
}
}
public ICommand AddItemCommand { get; private set; }
public ICommand DeleteItemCommand { get; private set; }
public MainViewModel()
{
Items = new ObservableCollection<Item>
{
new Item { Name = "John Doe", Age = 30 },
new Item { Name = "Jane Doe", Age = 28 }
};
AddItemCommand = new RelayCommand(AddItem);
DeleteItemCommand = new RelayCommand(DeleteItem);
}
private void AddItem()
{
Items.Add(new Item());
}
private void DeleteItem()
{
if (SelectedIndex >= 0)
{
Items.RemoveAt(SelectedIndex);
}
}
public int SelectedIndex { get; set; }
}
RelayCommand (Command Pattern):
using System;
using System.Windows.Input;
public class RelayCommand : ICommand
{
private readonly Action<object> _execute;
private readonly Predicate<object> _canExecute;
public RelayCommand(Action<object> execute, Predicate<object> canExecute = null)
{
_execute = execute ?? throw new ArgumentNullException(nameof(execute));
_canExecute = canExecute;
CommandManager.RequerySuggested += OnRequerySuggested;
}
public bool CanExecute(object parameter)
{
return _canExecute == null || _canExecute(parameter);
}
public void Execute(object parameter)
{
_execute(parameter);
}
public event EventHandler CanExecuteChanged;
private void OnRequerySuggested(object sender, EventArgs e)
{
CanExecuteChanged?.Invoke(this, e);
}
}
7. How can I handle data validation asynchronously in a WPF DataGrid?
Answer: Handling asynchronous validation can be achieved by creating custom ValidationRule
classes that perform asynchronous checks. Here's an example:
Custom Validation Rule:
using System.Globalization;
using System.Threading.Tasks;
using System.Windows.Controls;
public class AsyncValidationRule : ValidationRule
{
public async override Task<ValidationResult> ValidateAsync(object value, CultureInfo cultureInfo)
{
await Task.Delay(1000); // Simulate async delay
if (string.IsNullOrEmpty((string)value))
{
return new ValidationResult(false, "Async validation error: Name cannot be empty.");
}
return ValidationResult.ValidResult;
}
}
XAML:
<Window.Resources>
<local:AsyncValidationRule x:Key="AsyncRule" />
</Window.Resources>
<DataGrid ItemsSource="{Binding Items}" AutoGenerateColumns="False" Margin="10">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}">
<DataGridTextColumn.EditingElementStyle>
<Style TargetType="TextBox">
<Setter Property="Validation.ErrorTemplate">
<!-- ErrorTemplate Here -->
</Setter>
<Setter Property="ValidationRules">
<Setter.Value>
<ValidationRuleCollection>
<local:AsyncValidationRule />
</ValidationRuleCollection>
</Setter.Value>
</Setter>
</Style>
</DataGridTextColumn.EditingElementStyle>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
8. What is the best practice for data validation in MVVM with a DataGrid?
Answer: Best practices for data validation in MVVM with a DataGrid
include:
- Use Data Annotations: Implement
[Required]
,[StringLength]
,[Range]
, etc., for simple validation. - INotifyDataErrorInfo: For complex validation scenarios, use the
INotifyDataErrorInfo
interface. This provides detailed error information and is more powerful thanIDataErrorInfo
. - Validation Rules: Use
ValidationRule
for rules that are more specific to the UI and cannot be easily handled at the model level. - Commands: Use commands to handle actions like add, delete, and save. Commands can also trigger validation.
- Error Templates: Customize validation error templates to enhance user feedback.
- Async Validation: Where necessary, implement asynchronous validation rules.
9. How can I ensure data integrity and prevent invalid data submission in the DataGrid?
Answer: Ensuring data integrity in an editable DataGrid
involves:
- Validation on Commit: Validate data each time a user tries to commit changes. This can be done using
Validation.Error
events or in theDataGrid
's row commit logic. - Disable Save Button on Validation Errors: Disable the save or submit button if there are validation errors. This can be achieved using a
MultiBinding
or a property on the view model that checks for validation errors. - Data Binding: Use proper data binding with
UpdateSourceTrigger
set appropriately to ensure changes are reflected in the view model. - Custom Validation Logic: Implement custom validation logic in
IDataErrorInfo
,INotifyDataErrorInfo
, orValidationRule
classes. - Prevent Duplicate Entries: Implement checks to prevent duplicate entries, which can also be a part of validation logic.
10. What are some common pitfalls to avoid when implementing validation in a WPF DataGrid?
Answer: Common pitfalls to avoid when implementing validation in a WPF DataGrid
include:
- Not Using Proper Validation Mechanisms: Avoid using only
DataAnnotations
for complex validation. UseINotifyDataErrorInfo
for more powerful scenarios. - Ignoring Validation Errors: Ensure that validation errors are properly displayed to the user and that actions like saving data are not allowed if there are errors.
- Performance Issues: Be mindful of performance when performing validation, especially asynchronous validation. Avoid unnecessary checks and debounce user input if needed.
- Not Binding Validation Rules Correctly: Ensure that validation rules are properly added to bindings and that the
Validation.ErrorTemplate
is correctly set up. - Neglecting UI Feedback: Provide clear and actionable feedback to users about validation errors. This helps in resolving issues and maintaining data integrity.
By understanding and addressing these questions, you can effectively implement editable DataGrid
controls with robust validation in WPF applications.