Проверка перекрестного свойства в WPF - PullRequest
1 голос
/ 11 февраля 2011

В настоящее время я использую интерфейс IDataErrorInfo для реализации проверки в приложении WPF.Индексатор, который является частью этого интерфейса, позволяет проверять одно свойство, например:

public string this[string columnName]
{
    get
    {
        switch (columnName)
        {
            case "LastName":
                if (string.IsNullOrEmpty(this.LastName))
                    return "LastName must not be empty.";
                break;
            // case, case, case, etc., etc.
        }
        return null;
    }
}

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

Что я могу сделать, если у меня есть правила проверки, которые не связаны строго с одним свойством?Например, если у доменной сущности, представляющей заказ, есть дата доставки и дата выставления счета, и я хочу подтвердить правило, что дата накладной должна быть позже или равна дате отправления?

Конечно, я мог бытакже принудительно установите это правило в индексаторе, проверив отношение дважды, один раз для columnName «ShippingDate» и один раз для columnName «InvoiceDate», а затем пометив ошибку звездочкой в ​​обоих полях ввода в пользовательском интерфейсе, например:

public string this[string columnName]
{
    get
    {
        switch (columnName)
        {
            case "ShippingDate":
            case "InvoiceDate":
                if (this.ShippingDate > this.InvoiceDate)
                    return "Invoice date must not be before shipping date.";
                break;
        }
        return null;
    }
}

Но я бы предпочел иметь проверку «уровня объекта» или «перекрестного свойства» независимо от индексатора (индексатор должен только сообщать о недопустимом «состоянии одного свойства») и отображать эти ошибки уровня объекта или отношения отдельно напользовательский интерфейс.

Я надеялся, что свойство Error интерфейса IDataErrorInfo может иметь эту цель проверки уровня объекта.WPF вызывает индексатор для проверки свойства, например, когда я указываю ValidatesOnDataErrors=True в выражении Binding в TextBox.Но я не смог найти способ, чтобы WPF вызывал свойство Error всякий раз, когда я изменял некоторые данные в моих полях ввода.Может быть, мое предположение о назначении этого свойства неверно?

Как реализовать проверку перекрестных свойств в WPF?

Спасибо за предложения заранее!

1 Ответ

2 голосов
/ 11 февраля 2011

Что касается моего вопроса, могу ли я настроить привязку так, чтобы WPF автоматически проверял свойство Error интерфейса IDataErrorInfo, я нашел следующий отрицательный ответ здесь :

Вопрос откто-то:

По сути, я хотел бы знать о свойстве Binding, которое будет запускать тестирование IDataErrorInfo.Error, так как ValidatesOnDataErrors вызывает тестирование IDataErrorInfo.Item.

Ответ службы поддержки Microsoft Online Community:

Установка свойства ValidatesOnDataErrors класса Binding только для тестов IDataErrorInfo.Item, но не для IDataErrorInfo.Error.

Класс Binding не предоставляет свойство для проверки IDataErrorInfo.Error в качестве свойства ValidatesOnDataErrors для проверки IDataErrorInfo.Item на данный момент.

Чтобы получить то, что вам нужно, мы должны установить привязку данных кIDataError.Error ...

Итак, свойство Error не имеет большего значения, чем определение моегоСобственное ручное свойство (например, CrossPropertyErrors) в доменных сущностях.WPF не поддерживает тестирование свойства Error простым встроенным способом.

Редактировать: приведенные выше цитаты относятся к марту 2008 года, поэтому, скорее всего, они связаны с .NET 3.5.Но я не смог найти никаких признаков того, что это изменилось в .NET 4.0.

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

Редактировать 2

Это выглядит примерно так:

Классы модели (или ViewModel):

public class SomeModel : NotificationObject, IDataErrorInfo
{
    private string _someProperty;
    public string SomeProperty
    {
        get { return _someProperty; }
        set
        {
            if (_someProperty != value)
            {
                _someProperty = value;
                RaisePropertyChanged("SomeProperty", "Error");
                // That's the key: For every changed property a change
                // notification also for the Error property is raised
            }
        }
    }
    // The above repeats for every property of the model

    #region IDataErrorInfo Member
    public string Error
    {
        get
        {
            var sb = new StringBuilder();
            // for example...
            if (InvoiceDate < ShippingDate)
                sb.AppendLine("InvoiceDate must not be before ShippingDate.");

            // more cross-property validations... We have only one Error
            // string, therefore we append the messages with
            // sb.AppendLine("Another message...") ... etc.
            // could all be moved into a separate validation class
            // to keep the model class cleaner

            return sb.ToString();
        }
    }

    public string this[string columnName]
    {
        get
        {
            switch (columnName)
            {
                case "ShippingDate":
                    // property-level validations
                case "InvoiceDate":
                    // property-level validations
                // etc.
            }
            return null;
        }
    }
    #endregion
}

NotificationObject реализует RaisePropertyChanged:

public abstract class NotificationObject : INotifyPropertyChanged
{
    #region INotifyPropertyChanged Member
    public event PropertyChangedEventHandler PropertyChanged;
    #endregion

    protected virtual void RaisePropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
            handler(this, new PropertyChangedEventArgs(propertyName));
    }

    protected void RaisePropertyChanged(params string[] propertyNames)
    {
        if (propertyNames == null)
            throw new ArgumentNullException("propertyNames");

        foreach (var name in propertyNames)
            RaisePropertyChanged(name);
    }

    // ...
}

Тогда в представлении свойство Error связано, например, с TextBlock, которое отображает ошибки проверки перекрестного свойства:

<TextBlock Text="{Binding SomeModel.Error}" TextWrapping="Wrap" ... />

Итак: каждое измененное свойство в модели будетуведомить механизм привязки WPF о (потенциальном) изменении свойства Error, в результате чего будет обновлен текст проверки перекрестного свойства.

...