Question

I want to have a button's IsEnabled to only be true if two textboxes don't have any errors (in this case, I just want them to both be numeric)

I tried setting the textboxes to NotifyOnValidationError=True, thinking this will raise an exception that bubbles up to a container control - I would then set the Button's IsEnabled to true based on the Validation.HasError attached property belonging to that control

I tried the following (stripped of formatting code) but it's not working (the button is enabled when I first start the application and the textboxes are empty - they are bound to nullable decimals)

<WrapPanel>
    <TextBox x:Name="txtDeltaMin"Text="{Binding Path=DeltaMinFilter, NotifyOnValidationError=True, ValidatesOnDataErrors=True, ValidatesOnExceptions=True}"></TextBox>
    <TextBox x:Name="txtDeltaMax" Text="{Binding Path=DeltaMaxFilter, NotifyOnValidationError=True, ValidatesOnDataErrors=True, ValidatesOnExceptions=True}"></TextBox>
    <Button x:Name="btnUpdateFilter" Click="btnUpdateFilter_Click" IsEnabled="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type WrapPanel}}, Path=(Validation.HasError)}">Filter</Button>
</WrapPanel>
Was it helpful?

Solution

You can use a MultiBinding. Should look something like this:

<TextBox Text="{Binding Path=DeltaMinFilter, NotifyOnValidationError=True, ValidatesOnDataErrors=True, ValidatesOnExceptions=True}" />
<TextBox x:Name="txtDeltaMax" Text="{Binding Path=DeltaMaxFilter, NotifyOnValidationError=True, ValidatesOnDataErrors=True, ValidatesOnExceptions=True}" />
<Button x:Name="btnUpdateFilter" Click="btnUpdateFilter_Click" Content="Filter">
    <Button.IsEnabled>
        <MultiBinding Converter="{StaticResource IsEnabledConverter}">
            <Binding ElementName="txtDeltaMin" Path="Validation.HasError" />
            <Binding ElementName="txtDeltaMax" Path="Validation.HasError" />
        </MultiBinding>
    </Button.IsEnabled> 
</Button>

Then you just need to make the IsEnabledConverter class by implementing IMultiValueConverter, and add it as a resource.

public class IsEnabledConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        foreach (var isValid in values)
            if (isValid as bool? == false) 
                return false;
        return true;
    }

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

OTHER TIPS

You could also create a CommandBinding as described in this MSDN article, then use the CanExecuteHandler to Enable/Disable the Button by setting CanExecute.

From second link:

Typically, a command source, such a MenuItem, will call the CanExecute method on a RoutedCommand to determine if the command can or cannot execute on the current command target. If CanExecute is set to false from an event handler, the command source will disable itself. For example, if a MenuItem is acting as the command source for a command and the command cannot execute on the current command target, then the MenuItem will gray itself out.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top