valor obtener llenar data como celda wpf validation datagrid wpfdatagrid idataerrorinfo

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 .

  1. En codigo

    YourGrid.RowValidationErrorTemplate = new ControlTemplate();

  2. En Xaml

    <DataGrid.RowValidationErrorTemplate> <ControlTemplate> </ControlTemplate> </DataGrid.RowValidationErrorTemplate>`

  3. 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); }

  4. 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í:

  1. El modelo implementa IDataErrorInfo
  2. 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>

  3. 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.