【问题标题】:Attached property bound to a list via converter通过转换器绑定到列表的附加属性
【发布时间】:2011-12-21 09:19:21
【问题描述】:

我想在我的 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 中的集合,并基于集合转换器更改返回值,附加属性回调不会像我预期的那样触发。我做错了什么?

【问题讨论】:

  • 默认有验证错误吗?
  • 一般情况下可能有,但在我的测试用例中没有:很快没有。但集合不是空的,只是空的。
  • 这就像他们的问题 - 它会接收 Collection.Add 事件吗?
  • 我需要习惯如何编写 cmets,所以再次:靠近一点。为了澄清这个场景:我的虚拟机中有一个验证器,如果视图发生变化,它就会运行。以下是相关代码:`patientMainViewModel.ValidationFailures.Clear();整数解析; if (!Int32.TryParse(patientMainViewModel.PatientAggRoot.Patient.Ssn, out parse)) { patientMainViewModel.ValidationFailures.Add(new ValidationFailure("SSN", "Taj szám nem csak számokból áll")); }` 现在的问题是,我的转换器也没有运行。你有想法吗?
  • 在 Mode 之后尝试绑定 PresentationTraceSources.TraceLevel=High。它有效吗?给它任何错误?它有什么显示吗?

标签: c# wpf binding mvvm attached-properties


【解决方案1】:

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

基本上,您的 CLR 属性应该只是 ObservableCollection<..> 依赖属性的包装器。不要忘记在构造函数中或直接从您的 DP 声明中初始化它。

【讨论】:

    【解决方案2】:

    仅当您的源属性发生更改时,转换器才会运行。这意味着当为ValidationFailures 属性引发上下文的PropertyChanged 事件时。

    在您的情况下,您希望每个ValidationFailures.Add() 都会自动发生这种情况。 Observable 集合可以为自己的属性如Count 通知集合变化和属性变化。但它们不会自动通知将集合作为其值本身的属性。

    您必须在每个 ValidationFailures.Add() 之后提高 OnPropertyChanged("ValidationFailures") 或更改您的逻辑以利用 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);
        }
    

    如果您需要更多帮助,请告诉我。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-02-07
      • 2016-06-23
      • 1970-01-01
      • 2013-07-27
      • 2011-11-01
      • 2011-02-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多