Reputation: 87
I am trying to validate Textbox in an WPF application that uses both Textboxes and DataGrid.
The validation fires at load - shows that textbox is empty. But when I actually type something into the textbox and then move to another one, it still shows as not valid - validation doesn't fire again.
Both the datagrid and the textboxes are using data binding in order to load and write data from database.
I created a validation rule based on this tutorial: https://learn.microsoft.com/en-us/dotnet/framework/wpf/data/how-to-implement-binding-validation.
And mostly this solution works for me - when I select a row in datagrid first, then start changing contents of textboxes - validation works. However, as mentioned previously - it doesn't properly work when the app starts and nothing is selected (and textboxes are empty).
Below fragments of my XAML file:
<Window.Resources>
<CollectionViewSource x:Key="printLogViewSource" d:DesignSource="{d:DesignInstance {x:Type local:PrintLog}, CreateList=True}" >
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Name"/>
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
<ControlTemplate x:Key="validationTemplate">
<DockPanel>
<TextBlock Foreground="Red" FontSize="20">!</TextBlock>
<AdornedElementPlaceholder/>
</DockPanel>
</ControlTemplate>
</Window.Resources>
<Grid Background="#FFE5E5E5" Width="Auto" DataContext="{StaticResource printLogViewSource}">
<GroupBox x:Name="groupBox" Header="Add/Update/Remove" Margin="10,573,10,15">
<Grid Margin="0,0,0,0">
<Grid x:Name="grid1" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Label Content="Name:" Grid.Column="0" HorizontalAlignment="Left" Margin="3" Grid.Row="0" VerticalAlignment="Center"/>
<TextBox x:Name="nameTextBox" Validation.ErrorTemplate="{StaticResource validationTemplate}" Grid.Column="1" HorizontalAlignment="Left" Height="23" Margin="3" Grid.Row="0" VerticalAlignment="Center" Width="120" AutomationProperties.IsRequiredForForm="True">
<TextBox.Text>
<Binding Path="Name"
Mode="TwoWay"
UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<local:ValRule ValidatesOnTargetUpdated="True"/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
</Grid>
<Grid x:Name="grid2" HorizontalAlignment="Left" Margin="191,10,0,0" VerticalAlignment="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Label Content="Person:" Grid.Column="0" HorizontalAlignment="Left" Margin="3" Grid.Row="0" VerticalAlignment="Center"/>
<TextBox x:Name="personTextBox" Grid.Column="1" HorizontalAlignment="Left" Height="23" Margin="3" Grid.Row="0" Text="{Binding Person, Mode=OneWay, NotifyOnValidationError=true, ValidatesOnExceptions=true, UpdateSourceTrigger=Explicit}" VerticalAlignment="Center" Width="120"/>
</Grid>
</Grid>
</GroupBox>
<DataGrid x:Name="printLogDataGrid" AutoGenerateColumns="False" IsReadOnly="True" EnableRowVirtualization="True" ItemsSource="{Binding Source={StaticResource printLogViewSource}, UpdateSourceTrigger=PropertyChanged}" Margin="10,10,10,141" RowDetailsVisibilityMode="VisibleWhenSelected" CanUserAddRows="False">
<DataGrid.Columns>
<DataGridTextColumn x:Name="printIDColumn" Binding="{Binding PrintID}" Header="Print ID" Width="Auto" IsReadOnly="True"/>
<DataGridTextColumn x:Name="nameColumn" Binding="{Binding Name}" Header="Name" Width="Auto"/>
<DataGridTextColumn x:Name="personColumn" Binding="{Binding Person}" Header="Person" Width="Auto"/>
</DataGrid.Columns>
<DataGrid.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<DataGridCell Content="{Binding Name}"/>
</StackPanel>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</DataGrid.GroupStyle>
</DataGrid>
</Grid>
Here's my ValidationRule:
class ValRule : ValidationRule
{
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
if ((string)value == "")
{
return new ValidationResult(false, "Field cannot be empty");
}
else
{
return ValidationResult.ValidResult;
}
}
}
And here's fragment of my MainWindow.xaml.cs:
public partial class MainWindow : Window
{
private printDBEntities _context = new printDBEntities();
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
_context.PrintLogs.Load();
System.Windows.Data.CollectionViewSource printLogViewSource = ((System.Windows.Data.CollectionViewSource)(this.FindResource("printLogViewSource")));
printLogViewSource.Source = _context.PrintLogs.Local;
printLogDataGrid.SelectedIndex = -1;
//I'm setting this value to -1 so that on load my TextBoxes are empty.
//If i don't add this, on load my textboxes have value corrseponding to first row of datagrid.
}
protected override void OnClosing(CancelEventArgs e)
{
base.OnClosing(e);
this._context.Dispose();
}
//Below normally are functions responsible for Add/Update/Remove buttons
}
Could the line printLogDataGrid.SelectedIndex = -1;
when window loads be partially responsible for this problem?
As mentioned in the code - if I skip this, first row of datagrid is selected on start, which means textboxes are populated with data at start as well - that would mean that user would have to delete text from each box in order to add new record.
EDIT
To clarify the situation - the textbox that is not working as expected is the nameTextBox - the only one with validation for the moment.
Upvotes: 0
Views: 1045
Reputation: 151
<TextBox x:Name="nameTextBox" Validation.ErrorTemplate="{StaticResource validationTemplate}" Grid.Column="1" HorizontalAlignment="Left" Height="23" Margin="3" Grid.Row="0" VerticalAlignment="Center" Width="120" AutomationProperties.IsRequiredForForm="True">
<TextBox.Text>
<Binding Path="Name"
Mode="TwoWay"
UpdateSourceTrigger="PropertyChanged"
NotifyOnValidationError="True"
ValidatesOnNotifyDataErrors="True">
<Binding.ValidationRules>
<local:ValRule ValidatesOnTargetUpdated="True"/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
NotifyOnValidationError and ValidatesOnNotifyDataErrors should do it.
Upvotes: 0