3
votes

I'm using a System.Windows.Controls.DataGrid (WPF, .NET 4.0, C#). If a cell's validation fails (HasErrors == TRUE) the OK button should be gray (IsEnabled = FALSE).

The DataGrid validation is performed using a ValidationRule.

I've read several closely related articles here on StackOverflow, but I'm still jammed. I think the problem is that the Validation is on the DataGridRow, but the OK button's IsEnabled binding is looking at the whole grid.

To see the error, add a third row to the grid, and put in an invalid number (e.g. 200).) Or just edit one of the two stock values to be invalid (less than 0, non-integer, or greater than 100).

Here is the xaml:

<Window x:Class="SimpleDataGridValidation.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:c="clr-namespace:SimpleDataGridValidation"
        Title="MainWindow"
        Height="350"
        Width="525">
  <Window.Resources>
    <c:BoolToOppositeBoolConverter x:Key="boolToOppositeBoolConverter" />
  </Window.Resources>

  <Grid>
    <Grid.RowDefinitions>
      <RowDefinition Height="*"></RowDefinition>
      <RowDefinition Height="Auto"></RowDefinition>
    </Grid.RowDefinitions>

    <DataGrid Name="myDataGrid"
              ItemsSource="{Binding}"
              AutoGenerateColumns="False">

      <DataGrid.Columns>
        <DataGridTextColumn Header="Time"
                            x:Name="TimeField">
          <DataGridTextColumn.Binding>
            <Binding Path="Time"
                     UpdateSourceTrigger="PropertyChanged">
              <Binding.ValidationRules>
                <c:TimeValidationRule />
              </Binding.ValidationRules>
            </Binding>
          </DataGridTextColumn.Binding>
        </DataGridTextColumn>
      </DataGrid.Columns>

      <DataGrid.RowValidationErrorTemplate>
        <ControlTemplate>
          <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"
                       VerticalAlignment="Center" />
          </Grid>
        </ControlTemplate>
      </DataGrid.RowValidationErrorTemplate>
    </DataGrid>

    <Button Name="btnOk"
            Width="40"
            Content="_OK"
            IsDefault="True"
            Grid.Row="1"
            Click="btnOk_Click"
            IsEnabled="{Binding ElementName=myDataGrid, Path=(Validation.HasError), Converter={StaticResource boolToOppositeBoolConverter}}">
    </Button>
  </Grid>
</Window>

And here is the C# code-behind:

using System;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;

namespace SimpleDataGridValidation
{
  public partial class MainWindow : Window
  {
    public ObservableCollection<CTransition> Transitions;

    public MainWindow()
    {
     InitializeComponent();

     Transitions = new ObservableCollection<CTransition>();
     Transitions.Add(new CTransition(10, 5));
     Transitions.Add(new CTransition(20, 7));
     myDataGrid.DataContext = Transitions;
    }

    private void btnOk_Click(object sender, RoutedEventArgs e)
    {
     this.Close();
    }
  }

  public class CTransition
  {
    public int Time { get; set; }
    public int Speed { get; set; }

    public CTransition()
    { }
    public CTransition(int thetime, int thespeed)
    {
     Time = thetime;
     Speed = thespeed;
    }
  }

  public class TimeValidationRule : ValidationRule
  {
    public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
    {
     if (value != null)
     {
       int proposedValue;
       if (!int.TryParse(value.ToString(), out proposedValue))
       {
        return new ValidationResult(false, "'" + value.ToString() + "' is not a whole number.");
       }
       if (proposedValue > 100)
       {
        return new ValidationResult(false, "Maximum time is 100 seconds.");
       }
       if (proposedValue < 0)
       {
        return new ValidationResult(false, "Time must be a positive integer.");
       }
     }
     // Everything OK.
     return new ValidationResult(true, null);
    }
  }

  [ValueConversion(typeof(Boolean), typeof(Boolean))]
  public class BoolToOppositeBoolConverter : IValueConverter
  {
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
     if (targetType != typeof(bool) && targetType != typeof(System.Nullable<bool>))
     {
       throw new InvalidOperationException("The target must be a boolean");
     }
     if (null == value)
     {
       return null;
     }
     return !(bool)value;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
     if (targetType != typeof(bool) && targetType != typeof(System.Nullable<bool>))
     {
       throw new InvalidOperationException("The target must be a boolean");
     }
     if (null == value)
     {
       return null;
     }
     return !(bool)value;
    }
  }

}
1
You could try adding a Style within your button and using a MultiDataTrigger based on the field you want to validate within your datagrid. Like so; <MultiDataTrigger><MultiDataTrigger.Conditions> <Condition Binding="{Binding ElementName=Time, Path=(Validation.HasError)}" Value="false"/>... Not sure if this will work, but might be worth a punt! Hope this helps.greg
gregory.bmclub: As far, as I know, ValidationRule is not called when used MultiBinding link1 and link2Anatoliy Nikolaev

1 Answers

5
votes

I got it to work by adding 3 things: