mr_kite
mr_kite

Reputation: 87

WPF Textbox validation does not fire after contents change

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

Answers (1)

GeorgeDuke
GeorgeDuke

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

Related Questions