7

I want the Button control's property to be IsEnabled="False" until a value is entered into a TextBox in the Window.

Code so far:

<Button
  Content="Click Me"
  Name="ClickMe"
  VerticalAlignment="Top"
  Click="ClickMe_Click">
  <Button.Style>
    <Style>
      <Style.Triggers>
        <DataTrigger
          Binding="{Binding ElementName=textBox, Path=Length}"
          <!-- or even: Binding="{Binding Path=textBox.Length}" -->
          Value="0">
          <Setter
            Property="Button.IsEnabled"
            Value="false" />
          </DataTrigger>
      </Style.Triggers>
    </Style>
  </Button.Style>
</Button>

Also, is it possible to have this Button control's IsEnabled property be based on 3 different TextBox controls all having values?

abatishchev
  • 98,240
  • 88
  • 296
  • 433
JohnB
  • 18,046
  • 16
  • 98
  • 110
  • 1
    Oops, I had already tried `Binding="{Binding ViewModelDataMember}" Value=""`, but `Value="{x:Null}"` was the key here. – JohnB Jan 11 '11 at 21:23

3 Answers3

7

Assuming you are employing a presentation model, such as a ViewModel, you should bind directly the data instead of the UI elements.

<Style.Triggers>
    <MultiDataTrigger>
        <MultiDataTrigger.Conditions>
            <Condition Binding="{Binding FirstName}" Value="{x:Null}" />
            <Condition Binding="{Binding MiddleName}" Value="{x:Null}" />
            <Condition Binding="{Binding LastName}" Value="{x:Null}" />
        </MultiDataTrigger.Conditions>
        <Setter Property="Button.IsEnabled" Value="False" />
    </MultiDataTrigger>
</Style.Triggers>

That said, if you are using a presentation model, you can always add a bool "EnableSave" property and handle all the presentation logic there instead of in the view itself.

update

As can be seen by following the comments, I mistakenly set this up to enable the Button when any TextBox has a value, but the requirement is that the Button be enabled when all TextBoxes have values.

Conceptually, all you have to do is reverse the conditions -- instead of "false if all conditions false," we want "true if all conditions true."

The catch is that there is no way to say "not null" in the XAML -- no way without an IValueConverter, that is. I'd create a NullToBoolConverter that returns false for null and true for != null.

Given such a converter:

<Style.Triggers>
    <MultiDataTrigger>
        <MultiDataTrigger.Conditions>
            <Condition Binding="{Binding FirstName,
                Converter={StaticResource NullToBoolConverter}}" Value="True" />
            <Condition Binding="{Binding MiddleName,
                Converter={StaticResource NullToBoolConverter}}" Value="True" />
            <Condition Binding="{Binding LastName,
                Converter={StaticResource NullToBoolConverter}}" Value="True" />
        </MultiDataTrigger.Conditions>
        <Setter Property="Button.IsEnabled" Value="True" />
    </MultiDataTrigger>
</Style.Triggers>
Community
  • 1
  • 1
Jay
  • 56,361
  • 10
  • 99
  • 123
  • `{x:Null}` fixed my code! (for one data member) *I do have a ViewModel, and in my real code I was binding to the data.* But `` = **THANKS!** – JohnB Jan 11 '11 at 21:33
  • *However,* needed to make it `Property="Button.IsEnabled"` – JohnB Jan 11 '11 at 21:34
  • 1
    @JohnB You can also specify that it is a `Button` style by declaring it with ` – Jay Jan 11 '11 at 21:36
  • FYI, I have to click on another `TextBox` after by final 3rd data member gets set, before the `Button` will wake up. Also, the button doesn't seem to go inactive when I clear a data member (setting value to `null`). Lastly, and you'll never believe this one, *my speakers generate a high frequency hum when I display data* in my `DataGrid` - if I minimize the app, or clear the data, the hum goes away! :o – JohnB Jan 11 '11 at 21:55
  • 1
    @JohnB Try adding `UpdateSourceTrigger=PropertyChanged` *in the binding*. The default for a `TextBox` is `LostFocus`. When you clear the text, you are probably setting the value to an empty string rather than `null`. You can use a converter to handle both cases, or add logic to the setter of the view-model that sets the value to `null` when `value == string.Empty`. And as for the datagrid and your speakers… well, I don't know. You could try asking on SuperUser.com. – Jay Jan 11 '11 at 22:16
  • @Jay: I did that (even figured out how to do it through Properties dialog), but now the `Button` becomes active even when the 1st `TextBox` gets a value. Also, I am pretty sure I'm using `null`. The speakers thing is probably from high radiation! That's the trade-off for using WPF. – JohnB Jan 12 '11 at 00:40
  • 1
    @JohnB Sorry, I lost track of the requirement -- I thought it was supposed to be enabled when any of the textboxes have values. I'll update the answer to match the requirement. – Jay Jan 12 '11 at 00:54
  • Unfortunately, it doesn't work. `Button` is disabled at program startup. I set a breakpoint in the `IValueConverter`, and it seems to get called correctly, and return the appropriate value. However, the `Button` gets enabled after only one condition is `true`. My converter works fine for *"false if all conditions false,"* but not for the opposite :( – JohnB Jan 12 '11 at 16:02
3

TextBox doesn't have a Length property. Setting your binding Path to Text.Length might work.

But a more-flexible solution would be to use a Converter, which can return true or false based on the value of the string passed into it. You can then bind to the TextBox's Text property, like so:

In your control's Resources:

<localNamespace:MyEmptyStringToBooleanValueConverter x:Key="myValueConverter"/>

Your DataTrigger definition then looks like this:

<DataTrigger Binding="{Binding ElementName=textBox, Path=Text, 
                               Converter={StaticResource myValueConverter}}"   
             Value="False">

As for your second request, you can use a MultiBinding. In this case, you must use a value converter to define how the results of the binding should be interpreted. See the linked tutorial for details.

Dan J
  • 16,319
  • 7
  • 50
  • 82
2

Using property x:Name="FirstName" and similarly for others following code will work to enable the button when the data is present in all the fields.

<Button.Style>
    <Style>
        <Style.Triggers>
            <DataTrigger Binding="{Binding ElementName=FirstName,Path=Text.Length, Mode=OneWay}" Value="0">
            <Setter Property="Button.IsEnabled" Value="False" />
            </DataTrigger>
            <DataTrigger Binding="{Binding ElementName=MiddleName,Path=Text.Length, Mode=OneWay}"  Value="0">
            <Setter Property="Button.IsEnabled" Value="False" />
            </DataTrigger>
            <DataTrigger Binding="{Binding ElementName=LastName,Path=Text.Length, Mode=OneWay}" Value="0">
            <Setter Property="Button.IsEnabled" Value="False" />
            </DataTrigger>                  
         </Style.Triggers>
     </Style>
 </Button.Style>
Pranjal Kothari
  • 88
  • 1
  • 10