Hi, All.
После настойчивых обращений разъяренных пользователей на тему "почему я могу ввести пустую строку" в Silverlight 4 приложении, решил таки сделать "правильную" валидацию на формах ввода данных. Мудрые люди посоветовали воспользоваться DataAnnotations.
Собственно, сделал в ViewModel:
private string _name;
[Required(ErrorMessage = "Name is required.")]
public string Name
{
get { return _name; }
set {
if (_canValidate)
{
Validator.ValidateProperty(value,
new ValidationContext(this, null, null) { MemberName = "Name" });
}
_name = value;
OnPropertyChanged("Name");
}
}
Во View:
<TextBox Name="tbxName" Grid.Row="0" Grid.Column="1" Margin="4" MinWidth="160">
<TextBox.Text>
<Binding Path="Name" ValidatesOnExceptions="True" ValidatesOnDataErrors="True" NotifyOnValidationError="True" ValidatesOnNotifyDataErrors="True" Mode="TwoWay">
</Binding>
</TextBox.Text>
</TextBox>
Запускаю, смотрю — ввел пустое значение в Name, перешел на другой контрол — все шикарно, Name подсветился красным, появился хинт "Name is required.". Просто праздник какой-то.
Но, думаю, надо ж как-то проверять не только при вводе в контрол, но и при сохранении данных (нажатии на кнопку Ok).
Делаю (по-быстрому) в форме:
private void OKButton_Click(object sender, RoutedEventArgs e)
{
var mm = this.DataContext as ViewModel;
try
{
mm.Validate();
mm.UpdateModel();
this.DialogResult = true;
}
catch ( ValidationException ex)
{
MessageBox.Show(ex.Message);
}
}
А во ViewModel:
public void Validate()
{
//Validator.ValidateObject(this, new ValidationContext(this, null, null), true);
var errors = new List<ValidationResult>();
var isValid = Validator.TryValidateObject(this, new ValidationContext(this, null, null), errors, true);
if(!isValid)
{
throw new ValidationException( string.Join( Environment.NewLine, errors.Select((e) => e.ErrorMessage )));
}
}
Собственно система то валидирует, но красивых рюшечек с подстветкой НЕТ! Вопрос, можно ли как-то красиво пнуть систему, чтобы она делала подстветку валидации не только при обновлении пропертей, но и про "внейшей" проверке?
PS. Надеюсь понятно объяснил, чего хочу

Все-таки бессонные ночи оставляют свой неизгладимый след...
Пока нашел тупое решение
1. Отказаться от Validator и перейти на INotifyDataErrorInfo
2. В View model Validate сделать принудительное обновление контролируемых свойств, что-то вроде
public bool Validate()
{
Name = Name;
if ( HasErrors) {
MessageBox.Show("Validation failed" );
return false;
}
return true;
}
Минус
— пока пропертя для сравнения надо руками прописывать (лень было автомат написать)
— почему-то хочется валидатор использовать.
Но в целом — работает.
Докопаться до сообщений об ошибках в контролях SL практически нереально. Единственное, что работает — это принудительный байндинг с exception в сеттере.
Даю свой вариант установки любого графического контроля в режим ошибки:
public static class UX
{
#region Error Property
public static string GetError(DependencyObject obj)
{
return (string)obj.GetValue(ErrorProperty);
}
public static void SetError(DependencyObject obj, string value)
{
obj.SetValue(ErrorProperty, value);
}
static readonly DependencyProperty ErrorFlagProperty = DependencyProperty.RegisterAttached("ErrorFlag", typeof(object), typeof(UX), null);
public static readonly DependencyProperty ErrorProperty = DependencyProperty.RegisterAttached("Error", typeof(string), typeof(UX), new PropertyMetadata(OnErrorChanged));
static void OnErrorChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
var control = obj as Control;
if (control == null) return;
if (args.NewValue == null)
obj.ClearValue(ErrorFlagProperty);
else
{
var binding = new Binding("Error")
{
Mode = BindingMode.TwoWay,
NotifyOnValidationError = true,
ValidatesOnExceptions = true,
UpdateSourceTrigger = UpdateSourceTrigger.Explicit,
Source = new ErrorMessage((string)args.NewValue)
};
BindingOperations.SetBinding(obj, ErrorFlagProperty, binding);
control.GetBindingExpression(ErrorFlagProperty).UpdateSource();
}
}
public class ErrorMessage
{
readonly string _error;
public ErrorMessage(string error)
{
_error = error;
}
public string Error
{
get
{
return _error;
}
set
{
throw new Exception(_error);
}
}
}
#endregion
}
Работает так:
UX.SetError(_myEditBox, "Товарищи! Не забываем вводить парол");