附加属性通过转换器绑定到列表

时间:2011-12-21 09:19:21

标签: c# wpf binding mvvm attached-properties

我想在我的WPF中扩展一些标准的UIElements。理想情况下,使用附加属性是一个很好的解决方案。但我没有成功。

在我的ViewModel中,我有一组自定义对象:

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

这就是我要绑定到我的附属财产的内容。在我看来,我还将它绑定为ListBox的ItemsSource。它显示了变化,以及一切正确,这就是为什么我认为集合通知良好。

在我看来,我用以下代码绑定它:

<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次,更改回调一次,但是在(表单加载)之后我更改了我的VM中的集合,并根据集合转换器更改返回值,附加属性回调不会像我预期的那样触发。我错了什么?

2 个答案:

答案 0 :(得分:1)

您必须将Observable集合创建为DP。 Beeing只是一个CLR属性,它无法报告其项目添加/删除事件,它只报告有关整个属性集。

基本上你的CLR属性应该只是你的ObservableCollection&lt; ..&gt;的包装器。依赖属性。不要忘记在建设者或您的DP声明中对其进行初始化。

答案 1 :(得分:0)

如果源属性发生更改,转换器将仅运行。这意味着为PropertyChanged属性引发了上下文的ValidationFailures事件。

在您的情况下,您希望每个ValidationFailures.Add()自动发生这种情况。可观察集合可以为其自己的属性(例如Count)通知集合更改和属性更改。但是他们不会自动将保存集合的属性作为其自身的价值。

您必须在每个OnPropertyChanged("ValidationFailures")之后提升ValidationFailures.Add()或更改您的逻辑以利用ValidationFailures的可观察性,即处理可观察集合的集合更改事件。< / p>

解决方案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);
    }

如果您需要更多帮助,请与我们联系。