I have a small Person Domain Model as follows:
public class Person
{
public Person()
{
FirstName = "John";
LastName = "Doe";
BirthDate = DateTime.Now.AddYears(-18);
}
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid PersonId { get; set; }
[MaxLength(25, ErrorMessage = "The First Name cannot be more than 25 characters in length.")]
[MinLength(3, ErrorMessage = "The First Name must be at least three characters.")]
[Required(ErrorMessage = "The First Name is required.", AllowEmptyStrings = false)]
[DisplayName("First Name")]
public string FirstName { get; set; }
[MaxLength(25, ErrorMessage = "The Last Name cannot be more than 25 characters in length.")]
[MinLength(3, ErrorMessage = "The Last Name must be at least three characters in length.")]
[Required(ErrorMessage = "The Last Name is required.", AllowEmptyStrings = false)]
[DisplayName("Last Name")]
public string LastName { get; set; }
[DisplayName("Full Name")]
public string FullName => string.Format("{0} {1}", FirstName, LastName);
[Date(ErrorMessage = "Age must be between 18 and 65 years of age")]
[DisplayName("Birth Date")]
[DataType(DataType.Date,ErrorMessage = "This is not a valid date.")]
[Required(ErrorMessage = "The Birth Date is Required")]
public DateTime BirthDate { get; set; }
[RegularExpression(@"^((?!000)(?!666)(?:[0-6]\d{2}|7[0-2][0-9]|73[0-3]|7[5-6][0-9]|77[0-2]))-((?!00)\d{2})-((?!0000)\d{4})$",
ErrorMessage = "The Social Security Number must be in the pattern xxx-xx-xxx and be a valid US SSN.")]
[DisplayName("Social Security #")]
[Required(ErrorMessage = "The Social Security Number is Required.",AllowEmptyStrings = false)]
public string SocialSecurityNumber { get; set; }
public override string ToString()
{
return FullName;
}
}
If I create a new MVC web site and scaffold my controller, all my validating attributes just work on the create new and edit screens.
However, if I needed a WinForm to do the same thing, I would need to "roll my own". I did just that.
My WinForm code-behind in all its glory:
public partial class AddPersonForm : Form
{
private readonly DemoContext _context;
public AddPersonForm()
{
_context = new DemoContext();
InitializeComponent();
}
protected override void OnLoad(EventArgs e)
{
_context.People.Load();
personBindingSource.DataSource = _context.People.Local.ToBindingList();
personBindingSource.RaiseListChangedEvents = true;
FirstNameTextBox.Validating += Input_Validating;
LastNameTextBox.Validating += Input_Validating;
BirthDateDateTimePicker.Validating += Input_Validating;
SocialSecurityNumberTextBox.Validating += Input_Validating;
personBindingSource.AddNew();
base.OnLoad(e);
}
private void personBindingNavigatorSaveItem_Click(object sender, EventArgs e)
{
personBindingSource.EndEdit();
int changes = _context.SaveChanges();
Debug.WriteLine("# of changes: " + changes);
}
private void Input_Validating(object sender, CancelEventArgs e)
{
// GetValidationErrors cannot see any errors unless the following line is present
personBindingSource.EndEdit();
IEnumerable<DbEntityValidationResult> results = _context.GetValidationErrors();
foreach (DbEntityValidationResult result in results)
{
// Stops at the first error and also cancels the endedit.
foreach (DbValidationError error in result.ValidationErrors)
{
Debug.WriteLine("Property: " + error.PropertyName);
if (error.PropertyName == "FirstName")
{
errorProvider1.SetError(FirstNameTextBox, error.ErrorMessage);
//e.Cancel = true;
return;
}
if (error.PropertyName == "LastName")
{
errorProvider1.SetError(LastNameTextBox, error.ErrorMessage);
//e.Cancel = true;
return;
}
if (error.PropertyName == "BirthDate")
{
errorProvider1.SetError(BirthDateDateTimePicker, error.ErrorMessage);
//e.Cancel = true;
return;
}
if (error.PropertyName == "SocialSecurityNumber")
{
errorProvider1.SetError(SocialSecurityNumberTextBox, error.ErrorMessage);
//e.Cancel = true;
return;
}
}
}
errorProvider1.Clear();
}
}
It may look bad, but it does work. How do I make my method Input_Validating()
more efficient so I don't have to write a new block for every input control on the form?