Validation on DataGrid - c#

I have a datagrid displaying ObservableCollection in my xaml. One of my validation rule on one of the columns is "Name cannot be blank" when the Name field is blank. It all works fine.
My problem is that when my Name field validation is triggered (if name was blank), the Name field is outlined with red box. Imagine at this stage the user fills in a name but the red box still remains even if you click on the other fields of the same row. The red box goes away only when the user clicks on a different row. Is there any way to make the red box disappear when the user click on different fields of the same row?
My xaml for the Name field is
<Window.Resources>
<Style x:Key="EditCellStyleError" TargetType="{x:Type TextBox}">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="CellStyleError" TargetType="{x:Type TextBlock}">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
The Name field
<DataGridTextColumn Header="Name" Binding="{Binding Name,ValidatesOnDataErrors=True, NotifyOnValidationError=True, ValidatesOnExceptions=True}" EditingElementStyle="{StaticResource EditCellStyleError}" ElementStyle="{StaticResource CellStyleError}"/>

I think you need to add UpdateSourceTrigger to your binding
eg
<DataGridTextColumn Header="Name"
Binding="{Binding Name,ValidatesOnDataErrors=True, NotifyOnValidationError=True,
ValidatesOnExceptions=True, UpdateSourceTrigger=LostFocus}" />
here Ive used LostFocus but PropertyChanged might be an option

UpdateSourceTrigger=PropertyChanged definitely helped. Another problem was to get rid of red exclamation mark. To achieve this I added a blank Rowvalidationtemplate
<DataGrid.RowValidationErrorTemplate>
<ControlTemplate>
</ControlTemplate>
</DataGrid.RowValidationErrorTemplate>

Related

DataTrigger to a control's Validation.HasError

I have a user control which uses the INotifyDataErrorInfo interface and it turns red when it has errors, inside of this user control I put a TextBlock and the following DataTrigger doesn't seem to work:
<TextBlock Text="{Binding DurationText}"
Grid.Row="1">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=ActivityUserControl, Path=(Validation.HasError)}"
Value="True">
<Setter Property="Foreground"
Value="White">
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
I made sure that the x:Name of my user control is correct (ActivityUserControl), the user control itself turns red when it has errors but the TextBlock's DataTrigger doesn't work (text stays black).
Your should set the Value of the DataTrigger to begin with:
<DataTrigger Binding="{Binding ElementName=ActivityUserControl, Path=(Validation.HasError)}"
Value="True">
...
You should also make sure that the ActivityUserControl is in the same namescope as the TextBlock and that it acually contains some validation errors.
You can solve the namescope issue by binding to the parent UserControl using the RelativeSource property:
Binding="{Binding Path=(Validation.HasError),
RelativeSource={RelativeSource AncestorType=UserControl}}"

InvalidOperationException in WPF form when trying to change cell color in DataGrid

I cannot say that I am new to WPF, because it would be too much. I just was given with WPF app to maintain...
I need to change particular cell color in DataGrid based on a value. I thought it would be easy, found that SO post: Change DataGrid cell colour based on values
.
Pasted where it belongs, which gave me the following:
<DataGrid x:Name="DgDevices" ItemsSource="{Binding}" BorderThickness="2,0,2,2" Cursor="Cross">
<DataGrid.ContextMenu>
<ContextMenu >
<MenuItem Header="Załóż Deblokadę" Click="InsertDBL" />
<MenuItem Header="Usuń Deblokadę" Click="RemoveDBL"/>
</ContextMenu>
</DataGrid.ContextMenu>
<DataGridTextColumn Binding="{Binding Name}">
<DataGridTextColumn.ElementStyle>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<Trigger Property="Text" Value="1">
<Setter Property="Background" Value="Black"/>
<Setter Property="Foreground" Value="White"/>
</Trigger>
</Style.Triggers>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
</DataGrid>
Now, when invoking Show method on this form, it gives me InvalidOperationException. I searched for explanation why this happens, but haven't found clear explanation.
Also, I know that Binding Name is placeholder for my binding (in <DataGridTextColumn Binding="{Binding Name}">), so I tired putting just Binding there (inspired by ItemsSource="{Binding}" in DataGrid node), but didn't solve the issue.
You are now adding DataGridTextColumn right into DataGrid itself, not to its columns list. Adding items directly and using ItemsSource are mutually exclusive, so InvalidOperationException is thrown (and you didn't intend to add column as item anyway). Instead, do it like this:
<DataGrid x:Name="DgDevices"
ItemsSource="{Binding}"
BorderThickness="2,0,2,2"
AutoGenerateColumns="False"
Cursor="Cross">
<DataGrid.ContextMenu>
<ContextMenu >
<MenuItem Header="Załóż Deblokadę" Click="InsertDBL" />
<MenuItem Header="Usuń Deblokadę" Click="RemoveDBL"/>
</ContextMenu>
</DataGrid.ContextMenu>
<DataGrid.Columns> <!-- add to columns -->
<DataGridTextColumn Binding="{Binding Name}">
<DataGridTextColumn.ElementStyle>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<Trigger Property="Text"
Value="1">
<Setter Property="Background"
Value="Black" />
<Setter Property="Foreground"
Value="White" />
</Trigger>
</Style.Triggers>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
Also, because you need to set AutoGenerateColumns to False, because otherwise DataGrid will automatically generate columns from your data source, in addition to columns you define manually, and you rarely need that.

fire event only from user request

Is the any way to make Checked and Unchecked only fire when button clicked by user?
IsChecked is bound to Playing property. This value may change from background (not by user). So it causes Checked and Unchecked events to fire again and conflict with other things.
When ever I try to write code to fix this it becomes real mess. Thanks for your help.
<ToggleButton x:Name="TogglePlay" Width="90"
Style="{DynamicResource TogglePlay}"
IsChecked="{Binding SessionData.Playing}"
Checked="TogglePlay_Checked" Unchecked="TogglePlay_OnUnchecked"/>
<StackPanel.Resources>
<Style x:Key="TogglePlay" TargetType="ToggleButton">
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked , ElementName=TogglePlay}" Value="False">
<Setter Property="Content" Value="Play" />
</DataTrigger>
<DataTrigger Binding="{Binding IsChecked , ElementName=TogglePlay}" Value="True">
<Setter Property="Content" Value="Pause" />
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Resources>
You can try to use binding mode "OneWayToSource"

WPF:Using checkbox IsChecked property in DataTrigger

I am trying to use WPF checkbox's IsChecked property in a DataTrigger.Based on the value i am setting particular DataGridRow's background.
My NOT WORKING code
<Style TargetType="{x:Type DataGridRow}">
<Setter Property="Background" Value="LightGray" />
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=chkbox, Path=IsChecked}" Value="true">
<Setter Property="Background" Value="LightCyan" />
</DataTrigger>
</Style.Triggers>
</Style>
<DataGrid x:Name="dataGrid1" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTemplateColumn MinWidth="40" Width="Auto" Header="Select">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox x:Name="chkbox" IsChecked="{Binding Selected, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"></CheckBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
Then I checked this link and changed the code as below and it is working fine.Here Selected is my public property.
<Style TargetType="{x:Type DataGridRow}">
<Setter Property="Background" Value="LightGray" />
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Selected}" Value="true">
<Setter Property="Background" Value="LightCyan" />
</DataTrigger>
</Style.Triggers>
</Style>
Please help me to understand why my original code is not working? am i missing anything.
google did not help.surprisingly there is no thread for this on SO also! Thank you for the help.
The original code is not working because you're trying to locate an object via ElementName which exists as a templated object, and thus isn't created until after the binding tries to resolve. Generally, you should only use ElementName when referring to ancestor objects in the visual tree, and not to children, particularly templated children.
As mentioned in comments, it would also not be possible to use a {RelativeSource FindAncestor... binding here because the CheckBox is a child, not an ancestor, of the DataGridRow

Need to control look of textbox in DataGridTemplateColumn

I have a WPF DataGrid with DataGridTextColumns next to DataGridTemplateColumns. The cells in the template columns contain a textbox which I disable when a checkbox is checked. My problem is the look/behavior of the cells in these two types of columns do not match when they are being edited. In my XAML below, I have set the background of my textbox in the template column to match the cell it is in, which is the way the textbox in a text column works. Now I need the textbox in the template column to turn white while being edited, as does the textbox in the text column.
Here is my XAML:
<DataGridTextColumn Header="Rh" MinWidth="50" Binding="{Binding HorizontalResistivity, StringFormat=N2, Mode=TwoWay}"/>
<DataGridTemplateColumn Header="Rh min" MinWidth="50" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox x:Name="Rmin" Text="{Binding HorizontalResistivityMin, StringFormat=N2, Mode=TwoWay}"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch" BorderThickness="0"
Background="{Binding Background, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGridCell}}}"/>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=HorizontalResistivityIsFixed}" Value="True">
<Setter TargetName="Rmin" Property="IsEnabled" Value="False" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
I have come reasonable close to the answer I need. I abandoned the DataGridTemplateColumn and reverted to the DataGridTextColumn I was using before it was determined that the data grid cell had to be disabled. Here is my XAML:
<DataGridTextColumn Header="Rh min" MinWidth="50" Binding="{Binding HorizontalResistivityMin, StringFormat=N2, Mode=TwoWay}">
<DataGridTextColumn.CellStyle>
<Style TargetType="DataGridCell">
<Style.Triggers>
<DataTrigger Binding="{Binding HorizontalResistivityIsFixed}" Value="True">
<Setter Property="IsEnabled" Value="false"/>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGridTextColumn.CellStyle>
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="HorizontalAlignment" Value="Center"/>
</Style>
</DataGridTextColumn.ElementStyle>
<DataGridTextColumn.EditingElementStyle>
<Style TargetType="TextBox">
<Setter Property="HorizontalAlignment" Value="Center"/>
</Style>
</DataGridTextColumn.EditingElementStyle>
</DataGridTextColumn>
Some explanation is in order. After I got the IsEnabled property correctly bound, I found that the TextBlock in the cell was no longer centered (it was aligned left), hence the ElementStyle section which aligned the TextBlock. Also during editing the cell contents would shift to the left. During editing the cell contents are a TextBox, so I have the EditingElementStyle section which aligns the TextBox.
That leave only one issue, while editing any TextBox in this column, the whole row expands in height, maybe 10%. This does not happen in any DataGridTextColumn that does not have the disabling feature. It also does not happen if I don't include the EditingElementStyle section. It seems that adding the DataGridTextColumn.CellStyle section messed with some defaults, and getting them all beck is a pain.

Categories