Блог Сергея Байдачного

Мой блог о технологиях

Silverlight 4: Обработка ошибок при связывании

leave a comment »

Silverlight 3 поддерживал специальный механизм проверки данных при связывании с элементами управления – ValidatesOnException и NotifyOnValidationError. Первое свойство объекта типа Binding обеспечивало возможность визуального отображения ошибки (например, красный прямоугольник вокруг текстового поля), а второе – обеспечивало механизм перехвата и обработки ошибки в коде. Сама ошибка связывания должна была инициироваться исключением внутри класса, описывающего данные. То есть, если Вы выполняли связывание свойства FirstName класса Employee с текстовым полем, то обработку значения должно было выполнять само свойство и выбрасывать исключения всякий раз, когда устанавливаемое значение выглядело не корректно.

В Silverlight 4 механизм обработки ошибок при связывании был расширен следующими свойствами: ValidatesOnDataErrors и ValidatesOnNotifyDataErrors

ValidatesOnDataErrors

В отличие от ValidatesOnException, тут можно не вмешиваться в свойства класса, а вынести всю логику проверки и выдачи сообщений в отдельный метод, определяемый интерфейсом IDataErrorInfo. Это развязывает руки разработчику, позволяя создавать расширения существующих классов, просто определив производный класс, реализующий дополнительный интерфейс.

Рассмотрим простой класс Employee, реализующий интерфейс IDataErrorInfo. Приведем весь код этого класса:

   1:  public class Employee: 
   2:     INotifyPropertyChanged, IDataErrorInfo
   3:  {
   4:      public event PropertyChangedEventHandler PropertyChanged;
   5:   
   6:      public void OnPropertyChanged(PropertyChangedEventArgs e)
   7:      {
   8:          if (PropertyChanged != null)
   9:              PropertyChanged(this, e);
  10:      }
  11:   
  12:      private string firstName;
  13:      private string lastName;
  14:      private int age;
  15:      private string email;
  16:      private double salary;
  17:   
  18:      public double Salary
  19:      {
  20:          get 
  21:          { 
  22:              return salary; 
  23:          }
  24:          set 
  25:          { 
  26:              salary = value;
  27:              OnPropertyChanged(new PropertyChangedEventArgs("Salary"));
  28:          }
  29:      }
  30:   
  31:      public string FirstName
  32:      {
  33:          get { return firstName; }
  34:          set 
  35:          { 
  36:              firstName = value; 
  37:              OnPropertyChanged(new PropertyChangedEventArgs("FirstName"));
  38:          }
  39:      }
  40:   
  41:      public string LastName
  42:      {
  43:          get { return lastName; }
  44:          set
  45:          {
  46:              lastName = value;
  47:              OnPropertyChanged(new PropertyChangedEventArgs("LastName"));
  48:          }
  49:      }
  50:   
  51:      public string EMail
  52:      {
  53:          get { return email; }
  54:          set
  55:          {
  56:              email = value;
  57:              OnPropertyChanged(new PropertyChangedEventArgs("EMail"));
  58:          }
  59:      }
  60:   
  61:      public int Age
  62:      {
  63:          get { return age; }
  64:          set
  65:          {
  66:              age = value;
  67:              OnPropertyChanged(new PropertyChangedEventArgs("Age"));
  68:          }
  69:      }
  70:   
  71:      public override string ToString()
  72:      {
  73:          return String.Format("{0} {1}", firstName, lastName);
  74:      }
  75:   
  76:      string errors = null;
  77:   
  78:      public string Error
  79:      {
  80:          get { return errors; }
  81:      }
  82:   
  83:      public string this[string columnName]
  84:      {
  85:          get
  86:          {
  87:              string result = null;
  88:              if (columnName == "Age")
  89:              {
  90:                  if (Age <= 0)
  91:                  {
  92:                      result = "Age should be positive!";
  93:                  }
  94:                  else if (Age <= 16)
  95:                  {
  96:                      result = "You are too young!";
  97:                  }
  98:              }
  99:              return result;
 100:          }
 101:      }
 102:  }

Как видите, проверка свойств выносится в реализацию индексатора, описываемого интерфейсом IDataErrorInfo. При этом, реализация проверки базируется на проверке свойства, которое уже установлено (пусть даже в неправильное значение).

Следовательно, данных подход правильно применять скорее для отображения предупреждений, а не для проверки критических ошибок. Так, в примере выше, мы могли бы вынести проверку свойства Age на негативное значение в исключение, а проверку на совершеннолетие оставить в индексаторе.

Чтобы инициировать выдачу сообщений об ошибках, описанных в индексаторе, нужно воспользоваться свойством класса BindingValidatesOnDataErrors:

<TextBlock Text="Age:" Grid.Row="3" Grid.Column="0"></TextBlock>
<TextBox Text=
   "{Binding Path=Age, Mode=TwoWay, ValidatesOnDataErrors=True,
     NotifyOnValidationError=True}"
   BindingValidationError="TextBox_BindingValidationError"
   Grid.Row="3" Grid.Column="1">
</TextBox>

Естественно, что ValidatesOnDataErrors и ValidatesOnExceptions не могут отрабатывать одновременно. Иными словами, если у Вас есть готовый класс, описывающий некоторый объект, при этом его свойства выполняют проверку данных и выбрасывают исключения, то ValidatesOnDataErrors использовать нельзя, так как он полностью игнорируется при появлении исключения (даже если ValidatesOnExceptions установлен в false).

 

ValidatesOnNotifyDataErrors

К сожалению, не всегда данные можно проверить сразу же на клиенте. Очень часто необходимо обратиться к источнику данных, который расположен на удаленном сервере. Лишь после этого можно сделать вывод о корректности данных. Естественно, что описанные выше механизмы не подходят для удаленной проверки данных, так как заблокируют интерфейс на время соединения с источником данных. Поэтому Silverlight 4 представляет новый механизм, позволяющий вести асинхронную проверку данных.

Чтобы обеспечить асинхронную проверку данных, класс, описывающий данные, должен реализовывать интерфейс INotifyDataErrorInfo. Тут описаны свойство HasErrors, определяющее наличие ошибок при асинхронной проверке данных, метод GetErrors, возвращающий объект перечислимого типа, содержащий ошибки, а также событие ErrorsChanged, которое инициируется при любом изменении коллекции ошибок.

Чтобы активировать работу интерфейса INotifyDataErrorInfo, необходимо воспользоваться свойством класса Binding – ValidatesOnNotifyDataErrors, установив его значение в true.

Особенностью этого подхода является возможность его комбинации с описанными выше подходами.

Реклама

Written by Sergiy Baydachnyy

15.03.2010 в 09:33

Опубликовано в SilverLight

Tagged with

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s

%d такие блоггеры, как: