Как переопределить сообщение об ошибке в элементе управления с привязкой к данным в WinForms? - PullRequest
1 голос
/ 19 февраля 2009

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

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

  1. Как переопределить сообщение об ошибке по умолчанию чем-то более интуитивным для конечного пользователя. Например, сообщение по умолчанию в вышеупомянутом сценарии: «Строка ввода не была в правильном формате», и я бы предпочел, чтобы она говорила что-то вроде: «Возраст должен быть положительным целым числом».

  2. Как легко расширить эту систему с помощью более сложной бизнес-логики. Например, описанный выше обработчик по умолчанию запрещает принимать строку в качестве числа, но по умолчанию не устанавливает возраст> 0. Я знаю, что могу переопределить onValidate для каждого элемента управления, но, похоже, должен быть более простой способ, основанный на модели данных, а не на отдельном элементе управления.

Я попытался обновить мои установщики следующим образом, надеясь, что сообщение будет отображаться во всех формах / привязках, которые используют этот объект модели, но это не работает:

Public Property Age() As Integer
  Get
    Return m_age
  End Get
  Set(ByVal Value As Integer)
    If Not IsNumeric(Value) And Value > 0 Then
      Throw New ArgumentException("Age must be a positive integer.")
    End If
    m_age = Value
  End Set
End Property

Другие идеи?

Ответы [ 3 ]

2 голосов
/ 19 февраля 2009

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

Это так близко, как я получил с этим:

Для объекта домена, к которому вы привязываетесь:

  • Реализуйте System.ComponentModel.IDataErrorInfo, возвращая удобные для пользователя сообщения.
  • Реализация INotifyPropertyChanged для оповещения источника привязки об изменениях.

Оставьте для ErrorProvider.DataSource значение BindingSource.

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

Добавьте привязки для ваших элементов управления вручную, чтобы отключить форматирование. Если вы этого не сделаете, программа форматирования сожрет все предоставленные вами сообщения об ошибках. Так что не используйте редактор свойств в VS. Вместо этого назначьте привязку аля:

TextBox1.DataBindings.Add("Text", BindingSource1, "Age", False, DataSourceUpdateMode.OnValidation)

Если вы выполните вышеизложенное, то ваш конечный пользователь будет правильно видеть сообщения об ошибках объектов вашей модели, если преобразование не вызывает ошибку. Если преобразование вызывает ошибку, они увидят сообщение, предоставленное вами в обработчике Validating, если оно было, или общее сообщение в противном случае.

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

1 голос
/ 19 февраля 2009

Здесь. Это должно сэкономить вам тонну работы.

Вкратце: для проверки привязанного элемента управления есть три события, которые вы хотите обработать.

  • Событие Control.Validating, которое проверяет данные, когда пользователь покидает элемент управления,
  • Событие Control.Validated, которое обновляет источник данных, если и только если он правильно проверен, и
  • Событие Binding.Parse, которое проверяет данные, когда что-либо еще изменяет данные в элементе управления (т. Е. Ваш код).

Чтобы гарантировать, что в источник данных записываются только действительные данные, автоматическое обновление связанных данных отключается при создании Binding - таким образом, данные only записываются в источник данных во время события Control.Validated.

И события Validating, и Parse помещают сообщение об ошибке проверки в ErrorProvider, присоединенный к границе Control; у вас может быть другой способ представления сообщений об ошибках, и в этом случае вам нужно будет изменить оба этих события.

Вы также можете обработать событие привязки Format, чтобы управлять представлением данных в поле в связанном элементе управления.

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

fc - это экземпляр класса, который оборачивает связанные с моим приложением объекты Control. Он имеет свойство Control (очевидно, обернутый элемент управления), а затем свойства Binding и ErrorProvider, использование которых показано ниже. Обратите внимание, что, поскольку этот код устанавливает привязку данных для вас, вы не настраиваете привязку элемента управления в конструкторе форм. Вам, строго говоря, не нужен этот класс для того, чтобы этот код работал, но он несколько упрощает код. (Весь код здесь взят из статического метода класса fc, которому был передан BindingSource, как показано.)

cm - это экземпляр класса, который содержит метаинформацию о столбце данных, к которому я привязываю элемент управления, а именно:

  • ColumnName, его имя в источнике данных (я, очевидно, привязываюсь к DataColumn),
  • PropertyName, имя свойства элемента управления, с которым оно связано (например, "Text"),
  • NullValue, как описано в документации для Binding.NullValue,
  • Format, метод, который форматирует внутреннее значение столбца для отображения в связанном элементе управления, и
  • Parse, метод анализа ввода во внутреннее значение столбца. Фактическая логика проверки для столбца живет здесь.

Очевидно, что это C #, поэтому вам нужно возиться с ним, чтобы заставить его работать в VB, но различия должны просто зависеть от синтаксиса.

 // add an ErrorProvider to the control so that we have a place to display
 // error messages
 fc.ErrorProvider = new ErrorProvider {BlinkStyle = ErrorBlinkStyle.NeverBlink};

 // create the Binding.  DataSourceUpdateMode.Never bypasses automatic updating
 // of the data source; data only gets written to the data source when the
 // column is successfully validated.
 fc.Binding = fc.Control.DataBindings.Add(
    cm.PropertyName,
    bindingSource,
    cm.ColumnName,
    true,
    DataSourceUpdateMode.Never,
    cm.NullValue);

 // this is called whenever the Binding pushes data back to the data source;
 // it parses the data in the control into an object that's returned in e.Value.
 fc.Binding.Parse += delegate(object sender, ConvertEventArgs e)
 {
     string property = fc.Binding.PropertyName;
     object unparsedValue = fc.Control.GetType().GetProperty(property).GetValue(fc.Control, null);

     string message;
     // note that we don't actually care about the parsed value if message
     // is not null (i.e. if the value is invalid).  by convention it's null,
     // but it won't ever get written back to the data source.
     object parsedValue = cm.Parse(unparsedValue, out message);
     if (message != null)
     {
         fc.ErrorProvider.SetError(fc.Control, message);
     }
     else
     {
         fc.ErrorProvider.Clear();
     } 
     e.Value = parsedValue ?? DBNull.Value;
  };

  // this is called whenever the user leaves the Control.
  fc.Control.Validating += delegate
                                     {
     string property = fc.Binding.PropertyName;
     object value = fc.Control.GetType().GetProperty(property).GetValue(fc.Control, null);
     string message;
     cm.Parse(value, out message);
     if (message != null)
     {
         fc.ErrorProvider.SetError(fc.Control, message);
     }
     else
     {
         fc.ErrorProvider.Clear();
     }
 };

 // this, combined with the DataSourceUpdateMode of Never, insures that the Control's
 // value only gets pushed out to the data source after validation is successful.
 fc.Control.Validated += delegate {
     fc.Binding.WriteValue();
 };

 // this is called whenever the Binding pulls data from the data source into 
 // the bound Control
 fc.Binding.Format += delegate(object sender, ConvertEventArgs e)
 {
     e.Value = cm.Format(e.Value);
 };
0 голосов
/ 19 февраля 2009

Бросьте ошибку обратно вызывающей стороне. Затем перехватите тип исключения, который вы хотите «переопределить». Вместо отображения этого сообщения об исключении выведите специальное исключение и вместо этого покажите его сообщение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...