ValidationRules in DataGrid - c#

I am populating a DataGrid with a DataSet.
I have my dataGrid enabled for validationRules by make in each DataGridColumn ValidatesOnDataError=True but when I added a DataGridRow that has DataErrors then the DataGridRow is blocked and I cannot do anything else neither correct the DataGridRow
<!--Template for DataGridRow in Error state-->
<ControlTemplate x:Key="BasicRowValidationErrorTemplate">
<Grid Margin="0,-2,0,-2" ToolTip="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGridRow}},
Path=(Validation.Errors)[0].ErrorContent}">
<Ellipse StrokeThickness="0" Fill="Red" Width="{TemplateBinding FontSize}" Height="{TemplateBinding FontSize}" />
<TextBlock Text="!" FontSize="{TemplateBinding FontSize}" FontWeight="Bold" Foreground="White" HorizontalAlignment="Center" />
</Grid>
</ControlTemplate>
<!--Style for DataGridCell in static mode-->
<Style x:Key="textBlockInError" TargetType="{x:Type TextBlock}">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={x:Static RelativeSource.Self},
Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
<!--Style for DataGridCell in edit mode-->
<Style x:Key="textBoxInError" TargetType="{x:Type TextBox}">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="Background" Value="Red"/>
<Setter Property="ToolTip"
Value="{Binding RelativeSource={x:Static RelativeSource.Self},
Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
And I have defined my Columns like
<DataGrid>
<DataGrid.RowValidationRules>
<DataErrorValidationRule ValidationStep="ComittedValue"/>
</DataGrid.RowValidationRules>
<DataGrid.Columns>
<DataGridTextColumn Header="Nombre" Binding="{Binding Nombre, UpdateSourceTrigger=PropertyChanged,
ValidatesOnDataErrors=True}" ElementStyle="{StaticResource textBlockInError}"
EditingElementStyle="{StaticResource textBoxInError}"/>
<DataGridTemplateColumn Header="Price">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Price, ValidatesOnDataErrors=True}" Style="{StaticResource textBlockInError}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type UserControl},AncestorLevel=2},
Path=DataContext.Prices}" DisplayMemberPath="DisplayMember"
SelectedValue="{Binding Price}" IsEditable="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type UserControl}, AncestorLevel=2},
Path=DataContext.IsComboBoxEditable, Mode=OneWay}"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
I have in my ViewModel
DataTable Price;
DataTable Prices;
Price has two columns Name and Price
and Prices has two columns DisplayMember and ValueMember
I get the Precios from a file, if the file is not loaded then I do not have precies to show so I made Editable my combobox when i do not have the file. And just set my selected value with the one that user wrote
private void ComboBox_KeyUp(object sender, System.Windows.Input.KeyEventArgs e)
{
ComboBox comboBox = sender as ComboBox;
if (comboBox.IsEditable)
{
comboBox.SelectedValue = (sender as ComboBox).Text;
}
}
Why is blocked my DataGridRow ?

Related

How to change background of the template item, on selecting DataGridCell

I need to change the background color of a Textbox that is inside DataGridTemplateColumn cell based on whether or not if the data cell is selected.
Currently what I managed to do is, changing the background color of the template cell, which became useless because of error management.
Thus, I want to change the background of the Textbox, and revert it back on selection changed.
<DataGrid.Resources>
<Style TargetType="{x:Type DataGridRow}">
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="DarkBlue"/>
<Setter Property="Foreground" Value="Black"/>
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTemplateColumn IsReadOnly="True" MinWidth="150" Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding Path=TwoDLineName, Mode=TwoWay,UpdateSourceTrigger=LostFocus, ValidatesOnDataErrors=True, NotifyOnValidationError=True}"
HorizontalAlignment="Stretch" Style="{StaticResource TextBoxValidated}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
You could use a Style with a DataTrigger that binds to the IsSelected property of the parent row:
<DataGridTemplateColumn IsReadOnly="True" MinWidth="150" Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding Path=TwoDLineName, Mode=TwoWay,UpdateSourceTrigger=LostFocus, ValidatesOnDataErrors=True, NotifyOnValidationError=True}">
<TextBox.Style>
<Style TargetType="TextBox" BasedOn="{StaticResource TextBoxValidated}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsSelected,
RelativeSource={RelativeSource AncestorType=DataGridRow}}"
Value="True">
<Setter Property="Background" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

ToolTip to show validation errors works for TextBlock bound to POCO but not Property within POCO. Why?

In my WPF MVVM project I use INotifyDataErrorinfo to handle validation within a DataGrid. I can successfully style error cells in my "Operator" column thus:
<DataGridTemplateColumn Header="Operator" Width="140">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Operator}">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding OperatorId, Converter={StaticResource IsOperatorIdNullConverter}}" Value="False">
<Setter Property="FontWeight" Value="Bold"/>
</DataTrigger>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip">
<Setter.Value>
<ToolTip DataContext="{Binding RelativeSource={RelativeSource Self}, Path=PlacementTarget}">
<ItemsControl ItemsSource="{Binding Path=(Validation.Errors)}" DisplayMemberPath="ErrorContent"/>
</ToolTip>
</Setter.Value>
</Setter>
<Setter Property="Background" Value="Salmon"/>
</Trigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<Grid>
<controls:AutoCompleteBox Text="{Binding Operator, UpdateSourceTrigger=LostFocus, Mode=TwoWay}"
ItemsSource="{Binding Path=Data.OperatorNames, Source={StaticResource proxy}}"
IsTextCompletionEnabled="True"
FilterMode="Contains"
MinimumPrefixLength="3"/>
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
In my "OperatorType" column however this same technique doesn't work. Errors are being detected and the system default error styling is shown but my custom styling isn't. The code is:
<DataGridTemplateColumn Header="Operator type" Width="140">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding OperatorType.OperatorTypeName}">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip">
<Setter.Value>
<ToolTip DataContext="{Binding RelativeSource={RelativeSource Self}, Path=PlacementTarget}">
<ItemsControl ItemsSource="{Binding Path=(Validation.Errors)}" DisplayMemberPath="ErrorContent"/>
</ToolTip>
</Setter.Value>
</Setter>
<Setter Property="Background" Value="Salmon"/>
</Trigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<Grid>
<controls:AutoCompleteBox ItemsSource="{Binding Path=Data.OperatorTypeNames, Source={StaticResource proxy}}"
ItemTemplate="{StaticResource AutoCompleteBoxItemOperatorTypeTemplate}"
SelectedItem="{Binding OperatorType, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"
ValueMemberPath="OperatorTypeName"
IsTextCompletionEnabled="True"
FilterMode="Contains"
MinimumPrefixLength="3"/>
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
The only differences as far as I can see are that:
The Text in "Operator" is using bound to a POCO (Operator) while for "OperatorType" it is bound to a POCO property (OperatorType.OperatorTypeName)
The AutoCompleteBox declarations are slightly different
I've tried numerous settings for the ToolTip DataContext but nothing seems to work.
Question
What do I need to change to get the "OperatorType" customised error styling to work?
Well, it was a bit of a journey, but the solution was to set a DataContext on the TextBlock:
<TextBlock DataContext="{Binding OperatorType}" Text="{Binding OperatorTypeName}">
This causes the Trigger to be pointed at the OperatorType POCO whereas the Text in the box is taken from OperatorType.OperatorTypeName.

WPF DataGrid trigger on other cell value

I have DataGrid:
<DataGrid x:Name="PART_DataGrid"
ItemsSource="{TemplateBinding Items}"
AutoGenerateColumns="False"
Margin="2,25,2,2">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Language" Width="SizeToCells" IsReadOnly="False">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox SelectedItem="{Binding Path=Language, Mode=OneWay}"
ItemsSource="{Binding LanguagesSource, RelativeSource={RelativeSource AncestorType=UserControl}}"
FontSize="16"
IsEnabled="False">
<ComboBox.Style>
<Style TargetType="ComboBox">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGridRow}}, Path=test }" Value="">
<Setter Property="IsEnabled" Value="True"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox.Style>
</ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Translation" Width="SizeToCells" IsReadOnly="False">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox x:Name="TranslationValueTB"
Text="{Binding Path=Value, UpdateSourceTrigger=PropertyChanged}"
BorderThickness="0"
FontSize="16" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
And I want to set that first column ComboBox will be enabled when second TextBox.Text is empty, I have tried different binding methods but none of them have worked. So how can I do that?
Bind to the Value source property that the TextBox in the second column is bound to:
<DataGridTemplateColumn Header="Language" Width="SizeToCells" IsReadOnly="False">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox SelectedItem="{Binding Path=Language, Mode=OneWay}"
ItemsSource="{Binding LanguagesSource, RelativeSource={RelativeSource AncestorType=UserControl}}"
FontSize="16"
IsEnabled="False">
<ComboBox.Style>
<Style TargetType="ComboBox">
<Style.Triggers>
<DataTrigger Binding="{Binding Value}" Value="{x:Null}">
<Setter Property="IsEnabled" Value="True"/>
</DataTrigger>
<DataTrigger Binding="{Binding Value}" Value="">
<Setter Property="IsEnabled" Value="True"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox.Style>
</ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
Also make sure that the class where the Value property is defined implements the INotifyPropertyChanged interface and raises the PropertyChanged event in the setter of the Value property.
Remove the IsEnabled = false from combobox and do the same in the setter as follows, and do the binding as like follows,
<ComboBox.Style>
<Style TargetType="ComboBox">
<Setter Property="IsEnabled" Value="False"/>
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGridRow}}, Path=Item.Name }" Value="{x:Static sys:String.Empty}">
<Setter Property="IsEnabled" Value="True"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox.Style>
Item.Name - > this is the property you need to check

Show “No record found” message on a WPF DataGrid when it's empty

I'm trying to display a "No record found" Message in my WPF Datagrid
I tried the solution from the same question here:
Show "No record found" message on a WPF DataGrid when it's empty
But so far I didn't find a way to add a empty line only when my ObservableCollection is empty.
This is the code for my datagrid:
<DataGrid
DataContext="{Binding RelativeSource={RelativeSource AncestorType=Window}}"
ItemsSource="{Binding CameraListObjCode}"
AutoGenerateColumns="False"
Height="145"
HorizontalAlignment="Left"
Margin="62,105,0,0"
Name="dataGrid1"
VerticalAlignment="Top"
Width="361"
IsReadOnly="True">
<DataGrid.Resources>
<conv:IntIsEqualOrGreaterThan x:Key="intIsEqualOrGreaterThan"/>
<conv:IntIsLessThan x:Key="intIsLessThan"/>
<Style TargetType="DataGrid">
<Setter Property="RowDetailsVisibilityMode" Value="Collapsed"></Setter>
<Style.Triggers>
<!--<DataTrigger Binding="{Binding Path=CameraListObjCode.Count, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}, Converter={StaticResource intIsEqualOrGreaterThan}, ConverterParameter=1}" Value="True">-->
<DataTrigger Binding="{Binding Path=CameraListObjCode.Count, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}, Converter={StaticResource intIsLessThan}, ConverterParameter=1}" Value="True">
<Setter Property="RowHeight" Value="0"></Setter>
<Setter Property="RowDetailsVisibilityMode" Value="Visible"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTextColumn Header="Camera Model:" Binding="{Binding CameraModel}" />
<DataGridTextColumn Header="Serial Nr:" Binding="{Binding SerialNr}" />
<DataGridTextColumn Header="IP Address:" Binding="{Binding IPAddress}" />
</DataGrid.Columns>
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="No Record Found" Visibility="Visible" />
</StackPanel>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
</DataGrid>
The Triggers all work as as expected. But the message is not displayed.
When I change the Trigger Converter from "intIsLessThan" to "intIsEqualOrGreaterThan" the Message is shown instead of the actual datagrid entry.
So I guess just the empty default row is missing. But how can I add an empty default row?
I hope anyone can help me with this problem.
I'm already searching for two days now to find a solution...
i use this in my project:
<Style x:Key="{x:Type ItemsControl}" TargetType="{x:Type ItemsControl}">
<Setter Property="Background" Value="Transparent"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Items.Count, RelativeSource={RelativeSource Self}}" Value="0">
<Setter Property="Background">
<Setter.Value>
<VisualBrush Stretch="None">
<VisualBrush.Visual>
<TextBlock Text="No record found"
FontFamily="{StaticResource FontFamily}"
FontSize="{StaticResource FontSize}"/>
</VisualBrush.Visual>
</VisualBrush>
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding Items, RelativeSource={RelativeSource Self}}" Value="{x:Null}">
<Setter Property="Background">
<Setter.Value>
<VisualBrush Stretch="None">
<VisualBrush.Visual>
<TextBlock Text="No record found"
FontFamily="{StaticResource FontFamily}"
FontSize="{StaticResource FontSize}"/>
</VisualBrush.Visual>
</VisualBrush>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
<Style x:Key="{x:Type DataGrid}" TargetType="{x:Type DataGrid}" BasedOn="{StaticResource {x:Type ItemsControl}}">
</Style>

How to make mutually Exclusive checkBoxes in WPF DataGrid Columns

I am having following DataGrid Code
<UserControl x:Class="abc.WPFApp.UCGrid"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:WPFtoolkit="http://schemas.microsoft.com/wpf/2008/toolkit"
xmlns:local="clr-namespace:abc.WPFApp">
<UserControl.Resources>
<!--Restrict editing based on IsVariable-->
<Style x:Key="CheckBoxCellStyle" TargetType="{x:Type CheckBox}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsVariable}" Value="true">
<Setter Property="IsEnabled" Value="false"/>
</DataTrigger>
</Style.Triggers>
<Setter Property="HorizontalAlignment" Value="Center"/>
</Style>
</UserControl.Resources>
<Grid>
<WPFtoolkit:DataGrid x:Name="UCdataGridView" ItemsSource="{Binding}"
CellStyle="{StaticResource defaultCellStyle}"
RowStyle="{StaticResource defaultRowStyle}"
ColumnHeaderStyle="{StaticResource defaultDataGridColumnHeader}"
SelectionUnit="FullRow"
IsSynchronizedWithCurrentItem="True"
RowBackground="White"
AlternatingRowBackground="AliceBlue"
AutoGenerateColumns="False" SelectionMode="Extended" RowHeaderWidth="20"
CanUserAddRows="True" CanUserDeleteRows="True" CanUserReorderColumns="False"
CanUserResizeColumns="True" AllowDrop="True" KeyUp="UCGridKeyUp" >
<WPFtoolkit:DataGrid.Columns>
<WPFtoolkit:DataGridCheckBoxColumn x:Name="dgChkRepeatingData" Binding="{Binding Path=MasterDataFlag}" MaxWidth="135" MinWidth="80"
Header="Repeating data" Visibility="Collapsed" IsReadOnly="{Binding (IsVariable)}"
EditingElementStyle="{StaticResource CheckBoxCellStyle}"
>
</WPFtoolkit:DataGridCheckBoxColumn>
<WPFtoolkit:DataGridCheckBoxColumn MaxWidth="100" Header="Max Element" x:Name="dgChkMaxElement"
Binding="{Binding Path=MaxElement}" MinWidth="70" Visibility="Collapsed"
EditingElementStyle="{StaticResource CheckBoxCellStyle}">
</WPFtoolkit:DataGridCheckBoxColumn>
<WPFtoolkit:DataGridCheckBoxColumn MaxWidth="100" Header="In For Loop" x:Name="dgChkInForLoop"
Binding="{Binding Path=InForLoop}" MinWidth="70" Visibility="Collapsed"
EditingElementStyle="{StaticResource CheckBoxCellStyle}">
</WPFtoolkit:DataGridCheckBoxColumn>
<WPFtoolkit:DataGridTextColumn x:Name="dgXPath" Binding="{Binding Path=XPath}" Header="XPath" Width="500"
Visibility="Collapsed" IsReadOnly="{Binding Path=IsVariable}"
EditingElementStyle="{StaticResource TextBoxCellStyle}"/>
</WPFtoolkit:DataGrid.Columns>
</WPFtoolkit:DataGrid>
This user control Grid comprised if three Columns which should be mutually exclusive.
I want to achieve this by creating Triggers in XAML itself , How can we do this ?
You can use radiobuttons with a checkbox style to achieve mutually exclusive behavior. If you have any problem binding radiobuttons: radiobutton-binding
<RadioButton IsChecked="{Binding VMProperty}" GroupName="IsSelectedGroup">
<RadioButton.Style>
<Style TargetType="{x:Type RadioButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="RadioButton">
<CheckBox IsChecked="{Binding IsChecked, Mode=TwoWay, RelativeSource={RelativeSource AncestorType=RadioButton}}" Content="{TemplateBinding Content}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</RadioButton.Style>
</RadioButton>
But, i agree with Jaster. If you have each row binded to a ViewModel, the place to put this logic restriction is the ViewModel.
i am trying by creating such Style for 3rd column "InFor" But it is not at all working
<Style x:Key="mutualCheckInForLoop" TargetType="{x:Type WPFtoolkit:DataGridCell}" BasedOn="{StaticResource {x:Type WPFtoolkit:DataGridCell}}">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=dgChkRepeatingData,Path=IsChecked}" Value="True">
<Setter Property="CheckBox.IsChecked" Value="False" />
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=dgChkMaxElement,Path=IsChecked}" Value="True">
<Setter Property="CheckBox.IsChecked" Value="False" />
</DataTrigger>
</Style.Triggers>
</Style>

Categories