Опубликован: 08.07.2011 | Доступ: свободный | Студентов: 1772 / 93 | Оценка: 4.15 / 4.08 | Длительность: 15:28:00
Лекция 3:

Базовые инструменты WPF

< Лекция 2 || Лекция 3: 12345 || Лекция 4 >

Преобразование и проверка данных

При выполнении привязки данных достаточно часто тип или представление объекта-источника и целевого объекта не совпадают, и встает задача преобразования данных. Преобразование данных может проводится неявно, когда целевой тип может представлять любое допустимое значение типа источника, но это не всегда может удовлетворять пользователя.

В технологии WPF преобразование данных используется для:

  • форматирования данных к строковому представлению;
  • создания специфических типов WPF;
  • условного изменения свойств элемента на основе привязанных данных.

Для проектирования преобразователя значений необходимо выполнить следующее:

  1. Создать класс, реализующий интерфейс IValueConverter.
  2. Добавить атрибут ValueConversion в объявление класса и специфицировать исходный и отображаемый формат.
  3. Реализовать метод Convert(), преобразующий данные из исходного формата в отображаемый формат.
  4. Реализовать метод ConvertBack(), выполняющий обратное преобразование значения данные из отображаемого форма в исходный формат.

В случае преобразования вещественного значения в строковое можно использовать метод ToString(), а для обратного преобразования - Single.Parse().

Спроектируем класс конвертора StringToFloatConvert для преобразования строковых данных в вещественное число.

[ValueConversion(typeof(float),typeof(string))]
public class StringToFloatConvert : IValueConverter
{
    public object Convert(object value, Type typeTarget,
       object param, CultureInfo culture)
    {
        if (value != null)
         return value.ToString();
        else
            return string.Empty;
    }
    public object ConvertBack(object value, Type typeTarget,
        object param, CultureInfo culture)
    {
        if ((value != null) && (value.ToString() != ""))
            return Single.Parse(value.ToString(), NumberStyles.Float);
        else
            return 0;
    }
}

Обратите внимание на то, что для преобразования строки данных в вещественное число используется метод Single.Parse(value.ToString(), NumberStyles.Float). Данный метод преобразует строковое представление числа в указанном стиле в эквивалентное ему число одиночной точности с плавающей запятой, то есть для ввода используется не точка, а запятая.

Для подключения преобразователя необходимо к дескриптору окна добавить атрибут с отображением пространства имен проекта на префикс пространства имен XML:

xmlns:s="clr-namespace:Wpf_Primer1"

Далее необходимо создать экземпляр класса StringToFloatConvert и присвоить его свойству Convert в привязке элемента TextBox:

<TextBox Grid.Column="1" Margin="5,5,28,15" Name="textBox1">
 <TextBox.Text>
  <Binding Path="Factor" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged">
      <Binding.Converter>
          <s:StringToFloatConvert />
      </Binding.Converter>
  </Binding>
 </TextBox.Text>
</TextBox>

Тестирование приложения показывает, что при вводе цифровой строки с запятой, то есть вещественного числа с плавающей запятой, например "0,3", выводится также строка "0,3" ( рис. 3.8). При вводе цифровой строки с точкой формируется программное прерывание методом Single.Parse(value.ToString(), NumberStyles.Float).

Проверка корректности привязки с преобразованием данных

Рис. 3.8. Проверка корректности привязки с преобразованием данных

Теперь вернемся к вопросу проверки достоверности вводимых данных. Логика проверки достоверности вводимых данных должна перехватывать некорректные значения и отвергать их.

Технология WPF предполагает два варианта проверки достоверности данных для перехвата неверных значений:

  • инициирование ошибки в объекте данных путем генерации исключения при установке свойства;
  • определение проверки достоверности на уровне привязки.

Спроектируем правило проверки достоверности ввода данных путем создания класса ValidationFactor, который должен быть наследником класса правил ValidationRule из пространства имен System.Windows.Control:

public class ValidationFactor: ValidationRule
{
  public float Min { get; set;}
  public float Max { get; set;}
  public override ValidationResult Validate(object value,
             System.Globalization.CultureInfo cultureInfo)
  {
    try
    {
      if (((string)value).Length > 0)
        Window1.Factor.Factor = Single.Parse((string)value, 
                                        NumberStyles.Float);
      if((string)value == "")
                    Window1.Factor.Factor = 0;
    }
    catch (Exception e)
    {
      return new ValidationResult(false, " Введен недопустимый символ! ");
    }
    if ((Window1.Factor.Factor < Min) || (Window1.Factor.Factor > Max))
      return new ValidationResult(false, " Вводимое значение вне диапазона от
                                              " + Min + " до " + Max + ". ");
    else
      return new ValidationResult(true, null);
   }
}

В классе ValidationFactor определены свойства Min и Max, описывающие минимальное и максимально возможное значения вводимого вещественного числа и переопределен метод Validate() для требуемой проверки достоверности. При проверке достоверности используют перегруженную версию метода Single.Parse(), принимающего значение из перечисления NumberStyles. Это связано с тем, что проверка достоверности всегда выполняется перед преобразованием данных. Если применяется средство проверки достоверности и преобразователь к одному полю, то нужно обеспечить успех проверки достоверности. Успех или неудача логики проверки достоверности определяется возвращаемым объектом ValidationResult. Свойство IsValid указывает на успех проверки достоверности, и если проверка не прошла, то свойство ErrorContent представляет объект, описывающий ошибку.

Создадим экземпляр класса ValidationFactor и присвоим его свойству ValidationRules в привязке элемента TextBox:

<TextBox Grid.Column="1" Margin="5,5,28,15" Name="textBox1">
    <TextBox.Text>
        <Binding Path="Factor" Mode="TwoWay"
                               UpdateSourceTrigger="PropertyChanged">
            <Binding.Converter>
                <s:StringToFloatConvert />
            </Binding.Converter>
            <Binding.ValidationRules>
                <s:ValidationFactor Min="0" Max="1"/>
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
</TextBox>

Свойствам Min и Max класса ValidationFactor определим значения соответственно 0 и 1.

Тестирование приложения показывает, что при вводе корректного значения приложение работает правильно, а при вводе некорректного символа или числа вне интервала [0,1] происходит подсвет рамки элемента TextBox красным цветом ( рис. 3.9).

Проверка корректности ввода символов

Рис. 3.9. Проверка корректности ввода символов

На рис. 3.9 показан ввод недопустимого нецифрового символа и недопустимого по значению числа.

Следует отметить, что коллекция Binding.ValidationRules может состоять из неограниченного числа правил. Когда значение фиксируется в источнике, WPF будет проверять каждое правило проверки достоверности по порядку. Если все проверки достоверности пройдут успешно, то WPF вызовет преобразователь данных и применит значения к источнику. Если проверка не пройдет, то текстовое поле будет очерчено красным цветом, будут установлены свойства HasError и Error и инициируется событие Error.

< Лекция 2 || Лекция 3: 12345 || Лекция 4 >
Александр Петров
Александр Петров

При загрузке данных из БД возникает исключение InvalidOperationException с сообщением: Элемент коллекции должен быть пустым перед использованием ItemsSource. Знаю, что для заполнения DataGrid можно использовать коллекции Items или ItemsSource, но одновременно их использовать нельзя: если задано значение для свойства ItemsSource и в коде C# добавляется элемент в Items, возникает исключение. 
Вопрос, как отследить и отключить добавление элемента в Items?

Максим Спиридонов
Максим Спиридонов

В пятой лекции на второй странице в компиляторе выскакивает ошибка в строчке :

ObjectQuery<Employee> employees = DataEntitiesEmployee.Employees;

Ошибка CS0029

Не удается неявно преобразовать тип "System.Data.Entity.DbSet<WpfApplProject.Employee>" в "System.Data.Entity.Core.Objects.ObjectQuery<WpfApplProject.Employee>".

в using прописал все как положено, здесь похоже именно с преобразованием типов проблемы