Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF multibinding button.isEnabled with textboxes

I am new to WPF and I couldn't find solution on the web. My problem is that I want my button to be enabled only when four textboxes will not have validity errors. My code is:

<Button Content="Action" Click="Action_Click" >
      <Button.IsEnabled>
          <MultiBinding Converter="{StaticResource ValCon}">
               <Binding ElementName="textBox1" />
               <Binding ElementName="textBox2"/>
               <Binding ElementName="textBox3"/>
               <Binding ElementName="textBox4"/>
          </MultiBinding>
       </Button.IsEnabled>
</Button>

and my multi value converter is like:

class ValidityConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        bool b = true;
        foreach (var x in values)
        {
            TextBox y = x as TextBox;
            if (Validation.GetHasError(y))
            {
                b = false;
                break;
            }
        }
        return b;
    }
    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

I have noticed that this is working only at the beginning. When user modifies one of the textboxes it doesn't. Does my value converter need to implement INotifyPropertyChange? What is the correct solution of doing something like this in WPF? Thanks for your help.

EDIT

I have already done something like this and it's working:

                       <Button Click="Button_Click"  >
                            <Button.Style>
                                <Style TargetType="Button">
                                    <Setter Property="IsEnabled" Value="False"/>
                                    <Style.Triggers>
                                        <MultiDataTrigger>
                                            <MultiDataTrigger.Conditions>
                                                <Condition Binding="{Binding Path=(Validation.HasError), ElementName=textBox}" Value="False"/>
                                                <Condition Binding="{Binding Path=(Validation.HasError), ElementName=textBox1}" Value="False"/>
                                                <Condition Binding="{Binding Path=(Validation.HasError), ElementName=textBox2}" Value="False"/>
                                                <Condition Binding="{Binding Path=(Validation.HasError), ElementName=textBox3}" Value="False"/>
                                            </MultiDataTrigger.Conditions>
                                            <Setter Property="IsEnabled" Value="True"/>
                                        </MultiDataTrigger>
                                    </Style.Triggers>
                                </Style>
                            </Button.Style>
                        </Button>
like image 358
Marcin Avatar asked May 06 '14 16:05

Marcin


3 Answers

What i have done is use your MultiTrigger, solution but in my case i wanted a button to get activated only when 3 textboxes had zero length so my solution worked with the code below..

<Button.Style>
   <Style TargetType="Button">
       <Setter Property="IsEnabled" Value="True"/>
           <Style.Triggers>
                 <MultiDataTrigger>
                     <MultiDataTrigger.Conditions>
                                <Condition Binding="{Binding Path=Text.Length, ElementName=activationKeyTextbox}" Value="0"/>
                     </MultiDataTrigger.Conditions>
                     <Setter Property="IsEnabled" Value="False"/>
                     </MultiDataTrigger>
                     <MultiDataTrigger>
                     <MultiDataTrigger.Conditions>
                                <Condition Binding="{Binding Path=Text.Length, ElementName=serialDayStartbox}" Value="0"/>
                     </MultiDataTrigger.Conditions>
                     <Setter Property="IsEnabled" Value="False"/>
                     </MultiDataTrigger>
                     <MultiDataTrigger>
                     <MultiDataTrigger.Conditions>
                        <Condition Binding="{Binding Path=Text.Length, ElementName=serialNumdaysbox}" Value="0"/>
                 </MultiDataTrigger.Conditions>
                 <Setter Property="IsEnabled" Value="False"/>
             </MultiDataTrigger>
        </Style.Triggers>
    </Style>
 </Button.Style>
like image 200
hyphestos Avatar answered Oct 24 '22 18:10

hyphestos


I would not implement IMultiValueConverter as we are not converting anything here. BindingGroup may serve your purpose.

Equally effective approach using MVVM/INotifyPropertyChanged can be as follows :-

  1. Bind the button's IsEnabled property to a boolean property(say IsValid).
  2. Bind all the textboxes to different properties.
  3. OnPropertyChange of any of these textboxes, invoke a common function(say Validate())
  4. IsValid can be set to true or false based on Validate(), which will in turn toggle(enable/disable) the state of button.
like image 44
rajibdotnet Avatar answered Oct 24 '22 17:10

rajibdotnet


I know this is old, but I was in a similar need and didn't want to write a bindinggroup and validation... Note this is my first wpf app and first time using mvvm!

<Button Name="AddBtn" Content="Add" Grid.Row="2" Grid.Column="2" Height="30" Width="100" Command="{Binding AddCmd}" CommandParameter="{Binding Pending}" >
  <Button.IsEnabled>
     <MultiBinding Converter="{StaticResource multiButtonEnabled}" >
          <Binding ElementName="tb1" Path="(Validation.Errors)[0]" />
          <Binding ElementName="tb2" Path="(Validation.Errors)[0]"  />
          <Binding ElementName="tb3" Path="(Validation.Errors)[0]" />
     </MultiBinding>
  </Button.IsEnabled>
</Button>

Converter looks like this

[ValueConversion(typeof(ValidationError), typeof(bool))]
class TxtBoxMultiEnableConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        int i=0;
        foreach (var item in values)
        {
            if (item as ValidationError != null)
            {
                i++;
            }
        }
        if (i!=0)
        {
            return false;
        }
        return true;
    }

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

I was using IDataErrorInfo to catch errors in the model, and was binding to a double. WPF is nice and throws parsing errors for you when binding, but they don't really give you a way to hook into them to change the message displayed to the user or give you a property to check to see if there was an error flagged which is annoying, but this lets you catch errors in parsing without having to check for parsing errors in the validation rules. I'm sure my ignorance is showing, but we all have to start somewhere!

like image 43
Icy Avatar answered Oct 24 '22 18:10

Icy