Прикачено свойство, обвързано със списък чрез конвертор

Бих искал да разширя някои стандартни 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 на прикаченото свойство се задейства 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 анализира; 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=Високо само във вашето обвързване след режим. Работи ли? Дава ли някакви грешки? Изобщо показва ли нещо?   -  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:

Предлагам ви да създадете функция във вашия модел на изглед пациентMainViewModel.

  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