obtener - Los errores de validación WPF DataGrid no se borran
obtener valor de celda datagrid wpf (14)
Así que tengo un WPF DataGrid
, que está vinculado a un ObservableCollection
. La colección tiene validación en sus miembros, a través de IDataErrorInfo
. Si edito una celda de forma que no sea válida, y luego la elimino de la pestaña antes de presionar enter, luego vuelvo y la hago válida, la celda dejará de mostrarse inválida, sin embargo, el "!" en la cabecera de la fila seguirá allí, y la ToolTip
hará referencia al valor anterior no válido.
En mi caso, funcionó muy bien cuando utilizamos la versión de DataGrid WPF3.5. Actualizamos a 4.0, luego dejó de restablecerse. Después de buscar en SO, google etc., me encontré con mi solución. Al establecer UpdateSourceTrigger = PropertyChanged en el enlace en DataGridTextColumn, lo solucioné por mí.
Me acabo de dar cuenta de que el signo de exclamación rojo no se borra al establecerlo en un valor correcto.
En mi caso, tuve que eliminar de la definición de enlace
UpdateSourceTrigger=PropertyChanged
Para mí, funciona con ambas definiciones:
<DataGridTextColumn
Header="Time, min"
x:Name="uiDataGridTextColumnTime"
Width="Auto"
CellStyle="{StaticResource ResourceKey=DataGridCellText}"
IsReadOnly="False">
<DataGridTextColumn.Binding>
<Binding Path="fTime" StringFormat="{}{0:0.00}">
<Binding.ValidationRules>
<Validation:CellDataInfoValidationRule ValidationStep="UpdatedValue"/>
</Binding.ValidationRules>
</Binding>
</DataGridTextColumn.Binding>
Y
<DataGridTextColumn
Header="Time, min"
x:Name="uiDataGridTextColumnTime"
Width="Auto"
CellStyle="{StaticResource ResourceKey=DataGridCellText}"
Binding="{Binding fTime, StringFormat={}/{0:0.00/}, ValidatesOnDataErrors=True}"
IsReadOnly="False">
Validación: CellDataInfoValidationRule es una clase personalizada y obténgala aquí
public class CellDataInfoValidationRule : ValidationRule
{
public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
{
// obtain the bound business object
BindingExpression expression = value as BindingExpression;
IDataErrorInfo info = expression.DataItem as IDataErrorInfo;
// determine the binding path
string boundProperty = expression.ParentBinding.Path.Path;
// obtain any errors relating to this bound property
string error = info[boundProperty];
if (!string.IsNullOrEmpty(error))
{
return new ValidationResult(false, error);
}
return ValidationResult.ValidResult;
}
}
Y su objeto de datos debe implementar IDataErrorInfo
Encontré la mejor respuesta que funcionó para mí. Simplemente borre RowValidationErrorTemplate
su DataGrid
.
En codigo
YourGrid.RowValidationErrorTemplate = new ControlTemplate();
En Xaml
<DataGrid.RowValidationErrorTemplate> <ControlTemplate> </ControlTemplate> </DataGrid.RowValidationErrorTemplate>`
A continuación, cree su propia Plantilla de error de validación de fila.
Si su elemento de datos es INotifyPropertyChanged
((INotifyPropertyChanged)i).PropertyChanged += this.i_PropertyChanged;`
entonces
private void i_PropertyChanged(object sender, PropertyChangedEventArgs e) { this.Dispatcher.BeginInvoke(new Action(() => { var row = this.ItemContainerGenerator.ContainerFromItem(sender) as DataGridRow; if (row == null) return; var Errs = IsValid(row); if (Errs.Count == 0) row.Header = null; else { // Creatr error template var gg = new Grid { ToolTip = "Error Tooltip" }; var els = new Ellipse { Fill = new SolidColorBrush(Colors.Red), Width = row.FontSize, Height = row.FontSize }; var tb = new TextBlock { Text = "!", Foreground = new SolidColorBrush(Colors.White), HorizontalAlignment = HorizontalAlignment.Center, FontWeight = FontWeights.Bold }; gg.Children.Add(els); gg.Children.Add(tb); row.Header = gg; } }), System.Windows.Threading.DispatcherPriority.ApplicationIdle); }
Escribe tu propio método IsValid, de la manera que te guste
He utilizado esta técnica que elimina RowValidationRules y, en su lugar, uso las validaciones de propiedad en un modelo de vista. Esto requiere variables estáticas y anotaciones de datos:
//uses Prism.MVVM for BindableBase and INotifyDataErrorInfo
private static int _xxStartNo;
private static int _xxEndNo;
// in property getter/setter
private int _startNo;
[CustomValidation(typeof(YourModel), "ValidateStartNoRange")]
public int StartNo
{
get
{
_xxStartNo=_startNo;
return _startNo;
}
set
{
..........
ValidateProperty("StartNo")
}
}
.......
public static ValidationResult ValidateStartNoRange(int number)
{
if(number > _xxEndNo)
{
return ValidationResult("Start No must be less than End No.";
}
return ValidationResult.Success;
}
Mi escenario era así:
- El modelo implementa
IDataErrorInfo
Regla de validación de fila personalizada basada en WPF DataGrid. Ejemplos prácticos: validación con IDataErrorInfo , que combinó todos los errores del modelo utilizando IDataErrorInfo.
<DataGrid.RowValidationRules> <local:RowDataInfoValidationRule ValidationStep="UpdatedValue" /> </DataGrid.RowValidationRules>
ValidatesOnDataErrors=True
,ValidatesOnExceptions=True
,NotifyOnValidationError=True
dentro del enlace (con el que comencé)
Esto causó acceso múltiple a mi motor de validación y, eventualmente, dejó mi DataGrid
en estado incoherente (notificación de error en el encabezado de la fila incluso cuando la fila es válida).
La solución fue eliminar interruptores del enlace (punto 3)
Sugiero leer también Borrando un error de validación de fila de DataGrid .
Mi solución fue implementar comentarios de validación de filas personalizados, similar a esta página en la sección Para personalizar la validación de filas . El error de la fila luego desaparece adecuadamente.
(También agregué RowHeaderWidth="20"
a la definición de DataGrid
, para evitar el desplazamiento de la tabla hacia la derecha la primera vez que aparece el signo de admiración.)
Mi solución fue simplemente eliminar la propiedad UpdateSourceTrigger = "LostFocus" de la declaración vinculante en cada datagridcolumn.
Mi solución no fue usar Validation.Errors, sino usar la propiedad DataGridRow.Item. Si su DataGrid está vinculado a objetos comerciales que implementan la interfaz IDataErrorInfo, puede agregar la propiedad IsNotValid (o IsValid) y asegurarse de que la propiedad Error devuelva todos los errores asociados con el objeto. A continuación, personalice el estilo predeterminado para DataGridRowHeader:
<Style x:Key="{x:Type DataGridRowHeader}" TargetType="{x:Type DataGridRowHeader}">
...
<Control SnapsToDevicePixels="false"
Visibility="{Binding RelativeSource={RelativeSource
AncestorType={x:Type DataGridRow}},
Path=Item.IsNotValid, Converter={StaticResource
Bool2VisibilityConverter}}"
Template="{Binding RelativeSource={RelativeSource
AncestorType={x:Type DataGridRow}},
Path=ValidationErrorTemplate}" />
...
</Style>
También en el estilo de DataGridRow, personalice ValidationErrorTemplate para que muestre el mensaje de error de DataGridRow.Item.Error proeprty.
No estoy usando IDataErrorInfo
o INotifyDataErrorInfo
y mi solución era cambiar mis enlaces de UpdateSourceTrigger="PropertyChanged"
a UpdateSourceTrigger="LostFocus"
Esto era lo único que
Si está utilizando ValidationRules en su definición de columna DataGrid y necesita las reglas de validación para ejecutar cada vez que la propiedad cambie (en la interfaz de usuario o en la propiedad) busque en la configuración ValidatesOnTargetUpdated="True"
en su ValidationRule
Ejemplo XAML:
<DataGridTextColumn Header="Name"
CellStyle="{StaticResource DGCellStyle}"
ElementStyle="{StaticResource DGTextColValidationStyle}"
EditingElementStyle="{StaticResource DGTextColEditValidationStyle}">
<DataGridTextColumn.Binding>
<Binding Path="Name" UpdateSourceTrigger="LostFocus">
<Binding.ValidationRules>
<ValidationResource:YourValidationRule ValidationStep="UpdatedValue" ValidatesOnTargetUpdated="True" />
</Binding.ValidationRules>
</Binding>
</DataGridTextColumn.Binding>
</DataGridTextColumn>
No usar Mode=TwoWay
para DataGridTextColumns
resuelve una versión del problema, sin embargo, parece que este problema puede aparecer de la nada por otras razones también.
(Cualquiera que tenga una buena explicación de por qué no usar Mode=TwoWay
resuelve esto en primer lugar, probablemente esté cerca de una solución a este problema)
Me pasó lo mismo con DataGridComboBoxColumn
así que traté de profundizar un poco más.
El problema no es la Binding
en el Control
que muestra la ErrorTemplate
dentro de DataGridHeaderBorder
. Está vinculando su Visibility
a Validation.HasError
para el ancestro DataGridRow
(exactamente como debería estar haciendo) y esa parte está funcionando.
Visibility="{Binding (Validation.HasError),
Converter={StaticResource bool2VisibilityConverter},
RelativeSource={RelativeSource AncestorType={x:Type DataGridRow}}}"/>
El problema es que el error de validación no se borra de DataGridRow
una vez que se resuelve. En mi versión del problema, DataGridRow
comenzó con 0 errores. Cuando ingresé un valor no válido, recibí 1 error, hasta ahora todo bien. Pero cuando resolví el error saltó a 3 errores, todos los cuales fueron iguales.
Aquí traté de resolverlo con un DataTrigger
que establecía ValidationErrorTemplate
en {x:Null}
si Validation.Errors.Count
no era 1. Funcionó muy bien para la primera iteración, pero una vez que borré el error por segunda vez estaba de vuelta. . Ya no tenía 3 errores, ¡tenía 7! Después de un par de iteraciones más, estaba por encima de 10.
También traté de borrar los errores manualmente haciendo UpdateSource
y UpdateTarget
en BindingExpressions
pero sin dados. Validation.ClearInvalid
tampoco tuvo ningún efecto. Y mirar el código fuente en el Toolkit no me llevó a ningún lado :)
Así que no tengo ninguna buena solución para esto, pero pensé que debería publicar mis hallazgos de todos modos ...
Mi única "solución alternativa" hasta el momento es simplemente ocultar el ErrorTemplate
en el DataGridRowHeader
<DataGrid ...>
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="ValidationErrorTemplate" Value="{x:Null}"/>
</Style>
</DataGrid.RowStyle>
<!-- ... -->
</DataGrid>
Ok, después de un trabajo una alteración de la solución de Synerggetic funcionó para mí, así es como lo implementé:
<Style x:Key="{x:Type DataGridRowHeader}" TargetType="{x:Type DataGridRowHeader}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Control SnapsToDevicePixels="true"
Visibility="{Binding RelativeSource={RelativeSource
AncestorType={x:Type DataGridRow}},
Path=Item.HasErrors, Converter={StaticResource
BooleanToVisibilityConverter }}"
Template="{Binding RelativeSource={RelativeSource
AncestorType={x:Type DataGridRow}},
Path=ValidationErrorTemplate}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
No olvides hacer referencia al BooleanToVisibilityConverter:
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
Solo el estilo de la fila no se ve (todavía) tan bueno como el predeterminado, estoy trabajando en ello.
Editar: por favor ayuda aquí
Si ve un número cada vez mayor de errores similares a Meleak, me interesaría saber cómo se completa su colección de errores. En la versión del problema de Meleaks, ve tres errores (y más) después de resolver los datos no válidos.
En mi código de validación de datos, elimino la instancia anterior de un error en particular y luego vuelvo a agregar cada vez que los datos cambian. Como referencia, aquí hay una muestra:
La fontanería de validación
#Region " Validation workers "
Private m_validationErrors As New Dictionary(Of String, String)
Private Sub AddError(ByVal ColName As String, ByVal Msg As String)
If Not m_validationErrors.ContainsKey(ColName) Then
m_validationErrors.Add(ColName, Msg)
End If
End Sub
Private Sub RemoveError(ByVal ColName As String)
If m_validationErrors.ContainsKey(ColName) Then
m_validationErrors.Remove(ColName)
End If
End Sub
Public ReadOnly Property [Error]() As String Implements System.ComponentModel.IDataErrorInfo.Error
Get
If m_validationErrors.Count > 0 Then
Return "Shipment data is invalid"
Else
Return Nothing
End If
End Get
End Property
Default Public ReadOnly Property Item(ByVal columnName As String) As String Implements System.ComponentModel.IDataErrorInfo.Item
Get
If m_validationErrors.ContainsKey(columnName) Then
Return m_validationErrors(columnName).ToString
Else
Return Nothing
End If
End Get
End Property
#End Region
Una propiedad que se valida
Private Sub OnZIPChanged()
Me.RemoveError("ZIP")
If _ZIP Is Nothing OrElse _ZIP.Trim = "" Then
Me.AddError("ZIP", "Please enter a ZIP Code")
Else
Select Case _ZIP.Length
Case 5
Case 10
Case Else
Me.AddError("ZIP", "Please enter a ZIP Code")
End Select
End If
OnPropertyChanged("CanShip")
End Sub
Por lo tanto, cuando se ejecuta el controlador de propiedades modificadas, si existe un error en el diccionario ValidationErrors, se elimina, se comprueba el valor y, si no coincide con los requisitos, se agrega un error al diccionario. Esto ayuda a garantizar que solo haya una instancia de error en el diccionario de errores de validación de entidades.
Tengo el mismo problema con la plantilla de error RowHeader que no se va. Estoy usando INotifyDataErrorInfo. Siguiendo con la investigación de Fredrik Hedblad, hice una solución; Modifiqué la plantilla DataGridRowHeader para usar un enlace múltiple para la visibilidad ValidationErrorTemplate:
<Style x:Key="DataGridRowHeaderStyle" TargetType="{x:Type DataGridRowHeader}">
<!--<Setter Property="Background" Value="{DynamicResource {ComponentResourceKey TypeInTargetAssembly=Brushes:BrushesLibrary1,
ResourceId=HeaderBrush}}"/>-->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridRowHeader}">
<Grid>
<Microsoft_Windows_Themes:DataGridHeaderBorder BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}"
IsPressed="{TemplateBinding IsPressed}" IsHovered="{TemplateBinding IsMouseOver}"
IsSelected="{TemplateBinding IsRowSelected}" Orientation="Horizontal"
Padding="{TemplateBinding Padding}" SeparatorBrush="{TemplateBinding SeparatorBrush}"
SeparatorVisibility="{TemplateBinding SeparatorVisibility}">
<StackPanel Orientation="Horizontal">
<ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="Center"
Width="15"/>
<Control SnapsToDevicePixels="false"
Template="{Binding ValidationErrorTemplate, RelativeSource={RelativeSource AncestorType={x:Type DataGridRow}}}">
<Control.Visibility>
<MultiBinding Converter="{StaticResource ValidationConverter}">
<Binding Path="(Validation.HasError)" RelativeSource="{RelativeSource AncestorType={x:Type DataGridRow}}"/>
<Binding Path="DataContext.HasErrors" RelativeSource="{RelativeSource AncestorType={x:Type DataGridRow}}"/>
</MultiBinding>
</Control.Visibility>
<!-- Original binding below -->
<!--Visibility="{Binding (Validation.HasError), Converter={StaticResource bool2VisibilityConverter},
RelativeSource={RelativeSource AncestorType={x:Type DataGridRow}}}">-->
</Control>
</StackPanel>
</Microsoft_Windows_Themes:DataGridHeaderBorder>
<Thumb x:Name="PART_TopHeaderGripper" Style="{StaticResource RowHeaderGripperStyle}" VerticalAlignment="Top"/>
<Thumb x:Name="PART_BottomHeaderGripper" Style="{StaticResource RowHeaderGripperStyle}" VerticalAlignment="Bottom"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
Esto se basa en los objetos vinculados que tienen una propiedad "HasErrors" con notificación de cambio. En mi proyecto, me he asegurado de que la propiedad HasErrors se actualice al generar PropertyChanged para HasErrors en el evento EndEdit del elemento.
intente eliminar el Mode=TwoWay
para cada DataGridTextColumns
de cada uno de los elementos de enlace.