我想在我的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中的集合,并根据集合转换器更改返回值,附加属性回调不会像我预期的那样触发。我错了什么?
答案 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);
}
如果您需要更多帮助,请与我们联系。