28
votes

I have couple of textboxes with custom validators:
(I don't mind if "wrong" data is sent back to object (the property is string), I just need to prevent the functionality of a button if there is an error, so if the binding is not the right place for that kind of validation please tell. I just like the Validation.ErrorTemplate support that i can use)

<ControlTemplate x:Key="validator" >
    <DockPanel LastChildFill="True">
       <TextBlock DockPanel.Dock="Right" Foreground="Red" FontSize="12pt">!</TextBlock>
       <Border BorderBrush="Red" BorderThickness="1.0">
            <AdornedElementPlaceholder />
       </Border>
    </DockPanel>
</ControlTemplate>

<TextBox Height="23" Width="150"  TextWrapping="Wrap"
         Validation.ErrorTemplate="{StaticResource validator}">
         <TextBox.Text>
            <Binding Path="StringProperty" UpdateSourceTrigger="LostFocus">
               <Binding.ValidationRules>
                   <local:NumbersOnly/>
               </Binding.ValidationRules>
            </Binding>
        </TextBox.Text>
</TextBox>

How can I disable specific button if any of the validation error is raised?

<Button Content="DO Work"  Height="57" HorizontalAlignment="Left"  Name="button1" VerticalAlignment="Top" Width="234" Click="button1_Click" />
6
Possible duplicate here: stackoverflow.com/questions/231052/…Damascus
the posts over there don't answer the question...anderi

6 Answers

63
votes

You can use MultiDataTrigger property in Style.Triggers of Button. Let's assume that we have a TextBox named "txtName". We have to disable button "btnSave" on the validation error of TextBox.

Here is what you can do:

<Button Content="Save" 
        Grid.Column="1"
        Grid.Row="3"
        HorizontalAlignment="Right"
        Height="23" 
        Name="btnSave" 
        Width="75"
        IsDefault="True"
        Command="{Binding SaveProtocolCommand}"
        Margin="3">
  <Button.Style>
    <Style TargetType="Button">
      <Setter Property="IsEnabled" Value="False"/>
      <Style.Triggers>
        <MultiDataTrigger>
          <MultiDataTrigger.Conditions>
            <Condition Binding="{Binding Path=(Validation.HasError), ElementName=txtName}" Value="False"/>
          </MultiDataTrigger.Conditions>
          <Setter Property="IsEnabled" Value="True"/>
        </MultiDataTrigger>
      </Style.Triggers>
    </Style>
  </Button.Style>
</Button>

Hope this will help you.

10
votes

CanExecute in MVVM is for authorization management but people use it for validation. The best way is to do it in XAML. You will need a converter if you have multiple fields to validate (InverseAndBooleansToBooleanConverter is my implementation for multiple Booleans values). Here is how to do so:

XAML code(I'm sorry if the XAML code does show because I could have it appear even if I tried):

<Button Name="Button_Test" Content="Test">
    <Button.IsEnabled>
        <MultiBinding Converter="{StaticResource InverseAndBooleansToBooleanConverter}" Mode="TwoWay">
            <Binding ElementName="TextBox_Field1" Path="(Validation.HasError)" />
            <Binding ElementName="TextBox_Field2" Path="(Validation.HasError)" />
            <Binding ElementName="TextBox_Field3" Path="(Validation.HasError)" />
        </MultiBinding>
    </Button.IsEnabled>
</Button>

The converter

public class InverseAndBooleansToBooleanConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (values.LongLength > 0)
        {
            foreach (var value in values)
            {
                if (value is bool && (bool)value)
                {
                    return false;
                }
             }
        }    
        return true;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
            throw new NotImplementedException();
    }
}
3
votes

Add this to your TextBlock:

Validation.Error="Save_Error"

CodeBehind (xaml.cs):

public partial class MyView : Window
{
    private int _noOfErrorsOnScreen = 0;

    public MyView()
    {
        InitializeComponent();
    }


    private void Save_Error(object sender, ValidationErrorEventArgs e)
    {
        if (e.Action == ValidationErrorEventAction.Added)
            _noOfErrorsOnScreen++;
        else
            _noOfErrorsOnScreen--;

        Save.IsEnabled = _noOfErrorsOnScreen > 0 ? false : true;

    }
}
0
votes

If you use MVVM then just implement a method CanExecute of interface ICommand. Button doesn't become disabled when command CanExecute is false

If you write your logic in codebehind then just use a property of a button IsEnabled:

xaml:

<Button Name=_btn/>

SomeForm.xaml.cs:

_btn.IsEnabled=false;
-1
votes

Just use this code:

IsEnabled="{Binding Path=(Validation.HasError),ElementName=textBox, Converter={StaticResource InvertBooleanConverter}}"