Disable a MenuItem when TextBox of UserControl is empty

206 views Asked by At

In my main Window I have a MenuItem and a UserControl. I would like to disable/enable the MenuItem if one of the TextBoxes inside the UserControl is empty/not empty respectively.

Given a UserControl named ContactDetails and a TexBox called ContactNameTextBox, here's my xaml code for the MenuItem:

<MenuItem x:Name="DeleteContact" 
          Header="Delete Contact" 
          IsEnabled="{Binding ElementName=ContactDetails.ContactNameTextBox,Path=Text.Length, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>

For some reason, the MenuItem always stays enabled. What am I missing?

2

There are 2 answers

0
Olaru Mircea On

You are binding to the length of the Text but you need a Converter from length to a bool, because IsEnabled property expects a bool.

public class NumToBoolConverter : IValueConverter
{

    public object Convert(object value, Type targetType, object parameter,    System.Globalization.CultureInfo culture) 
    { 
       if (value!=null && value is int ) 
       {
          var val = (int)value; 
          return (val==0) ? false : true; 
       } 
       return null; 
    } 


    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
    { 
       if (value!=null && value is bool ) 
       { 
         var val = (bool)value; return val ? 1 : 0;
       } 
       return null; 
    }
}

Add a local xmlns for this and a resource.

 xmlns:local="clr-namespace:YourNamespace"

and this is the reference to the converter class.

<local:NumToBoolConverter x:Key="NumToBoolConverter"/>

In your Binding section add this :

 Converter={StaticResource NumToBoolConverter}

This can be your final MenuItem definition:

        <MenuItem x:Name="DeleteContact" 
                  Header="Delete Contact"
                  IsEnabled="{Binding ElementName=ContactDetails.ContactNameTextBox,
                                      Path=Text.Length, 
                                      Converter={StaticResource NumToBoolConverter},
                                      Mode=TwoWay, 
                                      UpdateSourceTrigger=PropertyChanged}"/>
0
Liz On

There are a couple of problems with your binding. The first is that you specified a two-way binding. That implies that you want to write back to the 'length' property in your textbox. Since it is readonly you can't.

Normally you should get an error for this:

A TwoWay or OneWayToSource binding cannot work on the read-only property 'Length' of type 'System.String'.

Now strangely enough, the binding does work after that. But that is REALLY not the right way. The magic of .NET is allowing a 0 to be interpreted as 'false'. But it is not a safe binding. As Olaru said in his answer, the length property is an integer and the IsEnabled field is looking for a bool. What if you wanted to bind to the 'visibility' property?

So what is the best way to handle this then? Converters are definitely one choice, and in many cases the best choice. The advantage to converters is that they can be re-used in similar cases. We have a library full of converters that we use very often. Olaru has described how to do that, so I won't repeat what he has already said.

In some cases though, it is beneficial to know a different way. A datatrigger will allow you to do the same kind of thing as a converter. It is a one-way binding. Here is an example.

<MenuItem x:Name="DeleteContact"  Header="Delete Contact">
   <MenuItem.Style>
       <Style TargetType="{x:Type MenuItem}" BasedOn="{StaticResource {x:Type MenuItem}}">
         <Setter Property="MenuItem.IsEnabled" Value="true"/>
         <Style.Triggers>
           <DataTrigger Binding="{Binding Text.Length, ElementName=ContactNameTextBox}" Value="0">
               <Setter Property="MenuItem.IsEnabled" Value="false"/>
           </DataTrigger>
         </Style.Triggers>
       </Style>
    </MenuItem.Style>
  </MenuItem>

No code necessary!!

There are plenty of arguments about the pros and cons of converters and datatriggers. But the main thing is to know that there are more than one way to do what you are asking.