Silverlight ValidationSummary не сохраняет сбои для элементов ListBox с тем же именем - PullRequest
4 голосов
/ 08 июня 2011

Обзор:

У меня есть приложение Silverlight 4, где я вижу проблемное поведение из элемента управления ValidationSummary при использовании со ListBox, и я надеюсь, что кто-то может мне помочь.

На высоком уровне у меня есть ListBox, где каждая строка определяется (через ItemTemplate) с текстовым полем с именем "txtInput". Текстовое поле привязано (TwoWay) к свойству объекта, помеченному атрибутом DataAnnotation.Range. ListBox связан с коллекцией этих объектов. В том же родительском элементе управления у меня также есть элемент управления ValidationSummary.

Сценарий:

Представьте себе случай, когда в коллекции предметов есть два или более объектов. Пользователь увидит ListBox с несколькими строками, каждая из которых содержит текстовое поле. Если пользователь вводит недопустимые данные в первое текстовое поле, выдается исключение ValidationException, как ожидается, а элемент управления ValidationSummary отображает ошибку, как и ожидалось. Текстовое поле также получает стиль ошибки проверки (красная рамка).

Затем, если пользователь вводит недопустимые данные в текстовое поле второй строки (без фиксации данных в первом текстовом поле), второе текстовое поле также создает исключение ValidationException и получает стилевое сообщение об ошибке проверки (красная граница), как и ожидалось, HOWEVER элемент управления ValidationSummary показывает только один экземпляр сообщения об ошибке.

Затем, если пользователь исправляет одну (но не обе) из недопустимых текстовых записей, в фиксированном текстовом поле удаляется стиль проверки (красная граница), и поле ValidationSummary исчезает (что означает его думает, что все ошибки валидации были устранены и для .HasErrors установлено значение false). Во втором (все еще недействительном) текстовом поле по-прежнему применяется стилизация ошибки проверки (красная граница).

Я ожидаю, что оставшееся недействительное текстовое поле приведет к тому, что элемент управления ValidationSummary будет продолжать отображаться. Я предполагаю, что элемент управления ValidationSummary просто отслеживает сбои по имени свойства, и после успешной попытки установить свойство с таким именем он очищает маркер ошибки (то есть: он не учитывает случай, когда несколько экземпляров одноименные встречаются).

Требуется результат:

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

Может кто-нибудь сказать мне, как можно заставить элемент управления ValidationSummary учитывать несколько сбоев одного и того же (повторяющегося) текстового поля, или предоставить жизнеспособный альтернативный способ отключить кнопку Сохранить, когда эти сбои существуют? (примечание: в моем реальном приложении каждая строка имеет несколько элементов управления вводом, поэтому любое решение должно учитывать это)

Фрагмент XAML:

    <sdk:ValidationSummary x:Name="valSummary" />
    <ListBox ItemsSource="{Binding DomainObjectCollection, Mode=TwoWay, ValidatesOnDataErrors=True, ValidatesOnNotifyDataErrors=True, ValidatesOnExceptions=true, NotifyOnValidationError=true}" >
        <ListBox.ItemTemplate>
            <DataTemplate>
                <TextBox Name="txtInput" Text="{Binding DecimalValue, Mode=TwoWay, ValidatesOnDataErrors=True, ValidatesOnNotifyDataErrors=True, ValidatesOnExceptions=true, NotifyOnValidationError=true}"/>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>

    <Button x:Name="btnSave" Content="Save" Command="{Binding SaveButtonCommand}"  IsEnabled="{Binding HasErrors, ElementName=valSummary, Converter={StaticResource NotBoolConverter}}" />

Классы объектов домена:

[System.Runtime.Serialization.CollectionDataContractAttribute()]
public partial class DomainObjectCollection : System.Collections.ObjectModel.ObservableCollection<DomainObject>
{
}

[System.Runtime.Serialization.DataContractAttribute()]
public partial class DomainObject : System.ComponentModel.INotifyPropertyChanged, System.ComponentModel.IDataErrorInfo, System.ComponentModel.INotifyDataErrorInfo
{

    private int DomainObjectId_BackingField;

    private decimal DecimalValue_BackingField;

    private System.Collections.Generic.Dictionary<string, System.Collections.Generic.List<object>> _errors;

    [System.Runtime.Serialization.DataMemberAttribute()]
    public virtual int DomainObjectId
    {
        get { return this.DomainObjectId_BackingField; }
        set 
        {
            if (!DomainObjectId_BackingField.Equals(value))
            {
                this.DomainObjectId_BackingField = value;
                this.RaisePropertyChanged("DomainObjectId");
            }
        }
    }

    [System.Runtime.Serialization.DataMemberAttribute()]
    [System.ComponentModel.DataAnnotations.RangeAttribute(typeof(decimal), "0", "100", ErrorMessage = "Value must be from 0 to 100.")]
    public virtual decimal DecimalValue
    {
        get { return this.DecimalValue_BackingField; }
        set
        {
            if (!DecimalValue_BackingField.Equals(value))
            {
                this.DecimalValue_BackingField = value;
                this.RaisePropertyChanged("DecimalValue");
            }
        }
    }

    string System.ComponentModel.IDataErrorInfo.Error
    {
        get { return string.Empty; }
    }

    string System.ComponentModel.IDataErrorInfo.this[string propertyName]
    {
        get
        {
            var results = Validate(propertyName);
            return results.Count == 0 ? null : string.Join(System.Environment.NewLine, results.Select(x => x.ErrorMessage));
        }
    }

    private System.Collections.Generic.Dictionary<string, System.Collections.Generic.List<object>> Errors
    {
        get
        {
            if (_errors == null)
                _errors = new System.Collections.Generic.Dictionary<string, System.Collections.Generic.List<object>>();

            return _errors;
        }
    }

    bool System.ComponentModel.INotifyDataErrorInfo.HasErrors
    {
        get { return Errors.Count > 0; }
    }

    public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;

    public event System.EventHandler<System.ComponentModel.DataErrorsChangedEventArgs> ErrorsChanged;

    protected internal void RaisePropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
        }
    }

    private void Raise(string propertyName)
    {
        if (ErrorsChanged != null)
            ErrorsChanged(this, new System.ComponentModel.DataErrorsChangedEventArgs(propertyName));
    }

    System.Collections.IEnumerable System.ComponentModel.INotifyDataErrorInfo.GetErrors(string propertyName)
    {
        System.Collections.Generic.List<object> propertyErrors;

        if (Errors.TryGetValue(propertyName, out propertyErrors))
            return propertyErrors;

        return new System.Collections.Generic.List<object>();
    }

    public void AddError(string propertyName, object error)
    {
        System.Collections.Generic.List<object> propertyErrors;

        if (!Errors.TryGetValue(propertyName, out propertyErrors))
        {
            propertyErrors = new System.Collections.Generic.List<object>();
            Errors.Add(propertyName, propertyErrors);
        }

        if (propertyErrors.Contains(error))
            return;

        propertyErrors.Add(error);
        Raise(propertyName);
    }

    public void RemoveError(string propertyName)
    {
        Errors.Remove(propertyName);
        Raise(propertyName);
    }

    public virtual System.Collections.Generic.List<System.ComponentModel.DataAnnotations.ValidationResult> Validate(string propertyName)
    {
        var results = new System.Collections.Generic.List<System.ComponentModel.DataAnnotations.ValidationResult>();
        var propertyInfo = GetType().GetProperty(propertyName);

        if (propertyInfo == null)
            return results;

        RemoveError(propertyName);

        var context = new System.ComponentModel.DataAnnotations.ValidationContext(this, null, null)
        {
            MemberName = propertyName
        };

        if (!System.ComponentModel.DataAnnotations.Validator.TryValidateProperty(propertyInfo.GetValue(this, null), context, results))
        {
            foreach (var validationResult in results)
                AddError(propertyName, validationResult.ErrorMessage);
        }

        return results;
    }
}

1 Ответ

3 голосов
/ 20 сентября 2011

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

Вы можете посмотреть и попробовать, может ли это решить вашу проблему:

public class ValidationSummaryCountFixBehavior : Behavior<ValidationSummary>
{
    private Dictionary<string, ValidationSummaryItem> _validationErrors = new Dictionary<string, ValidationSummaryItem>();

    protected override void OnAttached()
    {
        base.OnAttached();

        AssociatedObject.Loaded += new RoutedEventHandler(AssociatedObject_Loaded);
    }

    void AssociatedObject_Loaded(object sender, RoutedEventArgs e)
    {
        var target = AssociatedObject.Target as FrameworkElement ?? VisualTreeHelper.GetParent(AssociatedObject) as FrameworkElement;
        if (target != null)
        {
            target.BindingValidationError += new EventHandler<ValidationErrorEventArgs>(target_BindingValidationError);
        }
        AssociatedObject.Loaded -= new RoutedEventHandler(AssociatedObject_Loaded);
    }

    void target_BindingValidationError(object sender, ValidationErrorEventArgs e)
    {
        FrameworkElement inputControl = e.OriginalSource as FrameworkElement;

        if (((e != null) && (e.Error != null)) && ((e.Error.ErrorContent != null) && (inputControl != null)))
        {
            string message = e.Error.ErrorContent.ToString();
            string goodkey = inputControl.GetHashCode().ToString(CultureInfo.InvariantCulture);
            goodkey = goodkey + message;

            if (e.Action == ValidationErrorEventAction.Added && ValidationSummary.GetShowErrorsInSummary(inputControl))
            {
                string messageHeader = null;
                ValidationSummaryItem item = new ValidationSummaryItem(message, messageHeader, ValidationSummaryItemType.PropertyError, new ValidationSummaryItemSource(messageHeader, inputControl as Control), null);
                _validationErrors[goodkey] = item;
            }
            else
            {
                _validationErrors.Remove(goodkey);
            }
        }

        UpdateDisplayedErrors();
    }

    private void UpdateDisplayedErrors()
    {
        AssociatedObject.Errors.Clear();
        foreach (ValidationSummaryItem item in _validationErrors.Values)
        {
            AssociatedObject.Errors.Add(item);
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...