Maldición esto tomó un tiempo para pensar, pero como siempre, ... comportamientos unidos al rescate.
Lo que estás viendo en esencia es el seguimiento de estado sucio. Hay muchas maneras de hacer esto usando su ViewModel, pero como no quería cambiar sus entidades, la mejor manera es usar comportamientos.
Primero, quite los ValidatesOnDataErrors de su enlace Xaml. Cree un comportamiento para el control en el que está trabajando (como se muestra a continuación para TextBox
) y en el evento TextChanged
(o el evento que desee) restablezca el enlace a uno que valida en los errores de datos. Simple en realidad.
De esta manera, sus entidades no tienen que cambiar, su Xaml se mantiene razonablemente limpio y usted obtiene su comportamiento.
Aquí es el comportamiento código-
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
namespace IDataErrorInfoSample
{
public static class DirtyStateBehaviours
{
public static string GetDirtyBindingProperty(DependencyObject obj)
{
return (string)obj.GetValue(DirtyBindingPropertyProperty);
}
public static void SetDirtyBindingProperty(DependencyObject obj, string value)
{
obj.SetValue(DirtyBindingPropertyProperty, value);
}
// Using a DependencyProperty as the backing store for DirtyBindingProperty. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DirtyBindingPropertyProperty =
DependencyProperty.RegisterAttached("DirtyBindingProperty", typeof(string), typeof(DirtyStateBehaviours),
new PropertyMetadata(new PropertyChangedCallback(Callback)));
public static void Callback(DependencyObject obj,
DependencyPropertyChangedEventArgs args)
{
var textbox = obj as TextBox;
textbox.TextChanged += (o, s) =>
{
Binding b = new Binding(GetDirtyBindingProperty(textbox));
b.ValidatesOnDataErrors = true;
textbox.SetBinding(TextBox.TextProperty, b);
};
}
}
}
Y el Xaml es bastante simple también.
<Window x:Class="IDataErrorInfoSample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:IDataErrorInfoSample"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Title="MainWindow"
Height="350"
Width="525">
<Window.DataContext>
<local:Person />
</Window.DataContext>
<StackPanel Margin="20">
<TextBox Height="20"
Margin="0,0,0,10"
local:DirtyStateBehaviours.DirtyBindingProperty="Name"
Text="{Binding Path=Name}">
</TextBox>
<Button Content="Go" />
</StackPanel>
HTH, Stimul8d.
Estoy poniendo una recompensa en esta pregunta con la esperanza de una solución no hacky, si es que existe. –
¿No puedes simplemente crear a la persona antes de llamar a InitializeComponent()? – markmnl
Recompensa adicional para obtener una buena solución no hacky. –