Прикрепленное свойство привязано к списку через конвертер

Я хотел бы расширить некоторые стандартные элементы UIElements в моем WPF. В идеале было бы хорошим решением использовать вложенные свойства. Но мне это не удалось.

В моей ViewModel есть набор настраиваемых объектов:

        private ObservableCollection<ValidationFailure> validationFailures = new ObservableCollection<ValidationFailure>();
    public ObservableCollection<ValidationFailure> ValidationFailures
    {
        get { return validationFailures; }
        set
        {
            validationFailures = value;
            OnPropertyChanged(() => ValidationFailures);
        }
    }

Это то, что я хотел бы привязать к своему Прикрепленному свойству. На мой взгляд, я также привязываю его как ItemsSource к ListBox. Он показывает изменения, и все правильно, поэтому я думаю, что коллекция хорошо уведомляет.

На мой взгляд, я привязываю его к следующему коду:

<TextBox x:Name="ssn" Grid.Row="0" Grid.Column="1"  Margin="10,0,0,0"

                     Text="{Binding PatientAggRoot.Patient.Ssn}" 
                     Background="{Binding Path=CheckSsnButtonBackground}"

                     Validation:ValidationErrorAttached.HasValidationErrors="{Binding ValidationFailures,Converter={x:Static Converters:ConvertersHolder.ValidationErrorsLookupConverter},ConverterParameter='SSN',Mode=OneWay}" 
                                  />

Мой конвертер выглядит так:

    public class ValidationErrorsLookupConverter : IValueConverter
{
    #region IValueConverter implementation
    public object Convert(object value, Type targetType,
                          object parameter, CultureInfo culture)
    {
        if (value != null)
        {
            var validationLookup = (ObservableCollection<ValidationFailure>)value;
            bool hasErrors = validationLookup.Any(vf => vf.Key == ((string) parameter));
            return hasErrors;
        }

        return null;
    }

    public object ConvertBack(object value, Type targetType,
                              object parameter, CultureInfo culture)
    {
        throw new NotImplementedException("Can't convert back");
    }
    #endregion
}

Я проверил параметр конвертера, он также корректно работает со списком; Наконец, мое прикрепленное свойство:

    public static readonly DependencyProperty HasValidationErrorsProperty = DependencyProperty.RegisterAttached("HasValidationErrors",  typeof(Boolean), typeof(ValidationErrorAttached),  new FrameworkPropertyMetadata(true,      FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnChangeCallBack, OnCoerceValueChanged));

    private static object OnCoerceValueChanged(DependencyObject d, object basevalue)
    {
        //throw new NotImplementedException();
        return basevalue;
    }

    private static void OnChangeCallBack(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        //throw new NotImplementedException();
        if (((bool)e.NewValue))
        {
            ((TextBox) d).BorderBrush = Brushes.Red;
        }
    }

    public static void SetHasValidationErrors(UIElement element, Boolean value)
    {
        element.SetValue(HasValidationErrorsProperty, value);
    }
    public static Boolean GetHasValidationErrors(UIElement element)
    {
        return (Boolean)element.GetValue(HasValidationErrorsProperty);
    }

Он находится в классе ValidationErrorAttached, который является классом Freezable.

Когда я открываю свою форму, содержащую текстовое поле выше, coervalue Attached Property запускается 2 раза, меняю обратный вызов один раз, но после того, как (форма загружается), я меняю коллекцию в моей виртуальной машине, и на основе коллекции конвертер меняет возвращаемое значение, обратные вызовы присоединенного свойства не срабатывают, как я ожидал. Что я сделал не так?


person adam_gav    schedule 21.12.2011    source источник
comment
Есть ли у вас по умолчанию ошибки проверки?   -  person    schedule 21.12.2011
comment
В общем, могло быть, но не в моем тестовом примере: вкратце, нет. но коллекция не пуста, а просто пуста.   -  person adam_gav    schedule 21.12.2011
comment
В этом их проблема - подхватывает ли он событие Collection.Add?   -  person    schedule 21.12.2011
comment
Мне нужно привыкнуть к написанию комментариев, поэтому еще раз: подойдите ближе. Чтобы прояснить сценарий: в моей виртуальной машине есть валидатор, который запускается, если что-то изменилось в представлении. Вот соответствующий код: `PatientMainViewModel.ValidationFailures.Clear (); int parse; if (! Int32.TryParse (PatientMainViewModel.PatientAggRoot.Patient.Ssn, out parse)) {PatientMainViewModel.ValidationFailures.Add (new ValidationFailure (SSN, Taj szám nem csak számokból áll)); } `Теперь проблема в том, что мой конвертер тоже не запускается. Есть идея?   -  person adam_gav    schedule 21.12.2011
comment
Попробуйте привязку PresentationTraceSources.TraceLevel = High только в привязке после Mode. Это работает? Выдает какие-то ошибки? Он вообще что-нибудь показывает?   -  person dowhilefor    schedule 21.12.2011


Ответы (2)


Коллекция Observable должна быть создана как DP. Будучи просто свойством CLR, он не может сообщать о своих событиях добавления / удаления элементов, он сообщает только обо всех наборах свойств.

По сути, ваше свойство CLR должно быть не более чем оболочкой для вашего свойства ObservableCollection ‹..> Dependency. Не забудьте инициализировать его либо в конструкторе, либо прямо из объявления DP.

person Community    schedule 21.12.2011

Конвертер будет работать ТОЛЬКО при изменении вашего исходного свойства. Это означает, что событие контекста PropertyChanged вызывается для свойства ValidationFailures.

В вашем случае вы ожидаете, что это произойдет автоматически для каждого ValidationFailures.Add(). Наблюдаемая коллекция может уведомлять об изменениях коллекции и изменениях свойств для своих собственных свойств, таких как Count. Но они не будут автоматически уведомлять свойство, содержащее коллекцию, как само ее значение.

Либо вам нужно повышать OnPropertyChanged("ValidationFailures") после каждого ValidationFailures.Add(), либо изменить логику, чтобы использовать наблюдаемость ValidationFailures, то есть путем обработки события изменения коллекции наблюдаемой коллекции.

Решение 1.

Я предлагаю вам создать функцию в вашей модели представления PatientMainViewModel.

  void CheckAndValidate()
  {
        this.ValidationFailures.Clear();
        int parse;
        if (!Int32.TryParse(
              this.PatientAggRoot.Patient.Ssn, out parse))
        { 
             this.ValidationFailures.Add(
                    new ValidationFailure("SSN", "Taj szám nem csak számokból áll"));
             this.OnPropertyChanged("ValidationFailures");
        }
  } 

Решение 2

Избавьтесь от конвертера и сделайте прикрепленную собственность, чтобы делать все торги.

    public static readonly DependencyProperty ValidationErrorsProperty
        = DependencyProperty.RegisterAttached(
            "ValidationErrors",
            typeof(ObservableCollection<ValidationFailure>),
            typeof(ValidationErrorAttached),
            new FrameworkPropertyMetadata(new ObservableCollection<ValidationFailure>(), OnChangeCallBack));

    private static void OnChangeCallBack(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var list = (ObservableCollection<ValidationFailure>)e.NewValue;
        if (list != null)
        {
            list.CollectionChanged +=
                delegate(
                    object sender,
                    System.Collections.Specialized.NotifyCollectionChangedEventArgs arg)
                    {
                        if (list.Count == 0)
                        {
                            ((TextBox) d).BorderBrush = null;
                        }
                        else
                        {
                            ((TextBox) d).BorderBrush = new SolidColorBrush(Colors.Red);
                        }
                    };
        }
    }

    public static void SetValidationErrors(DependencyObject element, ObservableCollection<ValidationFailure> value)
    {
        element.SetValue(ValidationErrorsProperty, value);
    }

    public static ObservableCollection<ValidationFailure> GetValidationErrors(DependencyObject element)
    {
        return (ObservableCollection<ValidationFailure>)element.GetValue(ValidationErrorsProperty);
    }

Сообщите мне, если вам понадобится дополнительная помощь.

person WPF-it    schedule 21.12.2011