ck84vi
ck84vi

Reputation: 1576

Change enabled property of a WPF control (button) based on textbox content

Im looking for a solution in WPF to change the IsEnabled property of a button based on the content of a textbox. The TextBox holds a numeric value. If the value is greater than a certain value the IsEnabled property of the button should be set to true, as long as it is below this value the property should be false. I have been looking around but couldn't find a proper solution. What i found here on CodeProject is almost what im looking for. But the problem is that this approach just checks if any content is in the textbox. But i need to check/compare the numeric content.

I would prefer to find a way to do it in XAML. Alternatively i could implement it also in my ViewModel. But i dont have an idea how to do it! I was thinking about to notify the button via my INotifyChanged event from the property that is shown in the textbox. But i couldnt find out how.

Followed some code. But, sorry, there is nothing beside the textbox and the button since i couldnt find a way to solve that.

<TextBox Name ="tbCounter" Text ="{Binding CalcViewModel.Counter, Mode=OneWay}" Background="LightGray" BorderBrush="Black" BorderThickness="1" 
              Height="25" Width="50"
              commonWPF:CTextBoxMaskBehavior.Mask="Integer"
              commonWPF:CTextBoxMaskBehavior.MinimumValue="0"
              commonWPF:CTextBoxMaskBehavior.MaximumValue="1000"
              IsReadOnly="True"/>
<Button Name="btnResetCount" Focusable="True" Content="Reset" Command="{Binding Path=CalcViewModel.ResetCounter}" Style="{StaticResource myBtnStyle}"
              Width="100" Height="25">

Is there a common way to set the IsEnabled property of a control based on a property/value in the XAML or in the ViewModel?

EDIT This is my ViewModel, i extracted the related members and properties only otherwise the post would be too long:

class CalcViewModel:INotifyPropertyChanged
{
    private CCalc _calc;

    public int Counter
    {
        get
        { return _calc.Counter; }
        set{ _calc.Counter = value;}
    }

    public event PropertyChangedEventHandler PropertyChanged;

    void ResetCounterExecute()
    { _calc.Counter = 0; }

    bool CanResetCounterExecute()
    {
        if (_calc.Counter > 0)
        { return true; }
        else
        { return false; }
    }

    public ICommand ResetCounter
    { get { return new RelayCommand(ResetCounterExecute, CanResetCounterExecute); } }

    public CCalcViewModel()
    {
        this._calc = new CCalcViewModel();
        this._calc.PropertyChanged += new PropertyChangedEventHandler(OnCalcPropertyChanged);
    }
    private void OnCalcPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        this.RaisePropertyChanged(e.PropertyName);
    }

    public void RaisePropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }

Upvotes: 0

Views: 7851

Answers (3)

David E
David E

Reputation: 1444

If you wish to do it directly in the XAML (I wouldn't necessarily recommend this, as validation should probably be done in the view model), you can also use a package, such as https://quickconverter.codeplex.com/ - this allows you to write some C# (ish) in a binding.

I've used it before, and it can make it pretty easy, eg you install the package, add the line to the very start of your application:

QuickConverter.EquationTokenizer.AddNamespace(typeof(object));

which adds the System namespace to QuickConverter (the line above works as object is in the System namespace), and then you can simply do:

IsEnabled="{qc:Binding 'Int32.TryParse($P) &amp;&amp; Int32.Parse($P) &gt;= 3', P={Binding ElementName=tbCounter, Path=Text}}"

If &amp; breaks your Intellisense, you can instead write:

IsEnabled="{qc:Binding 'Int32.TryParse($P) ## Int32.Parse($P) &gt;= 3', P={Binding ElementName=tbCounter, Path=Text}}"

(Where 3 is the value you're testing against).

EDIT:

Sorry, on re-reading you XAML, it can be written even more straightforwardly as follows:

IsEnabled="{qc:Binding '$P &gt;= 3', P={Binding CalcViewModel.Counter}}"

Upvotes: 2

C Bauer
C Bauer

Reputation: 5103

You want to combine an element property binding:

IsEnabled={Binding ElementName=Textbox, Path=Text}

With a valueconverter

IsEnabled={Binding ElementName=Textbox, Path=Text, Converter={StaticResource IsAtLeastValueConverter}}

IsAtLeastValueConverter.cs

namespace WpfPlayground
{
    public class IsAtLeastValueConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (System.Convert.ToInt32(value) > 5)
            {
                return true;
            }
            return false;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return Binding.DoNothing;
        }
    }
}

Oh I forgot you'll need to add this to your control:

<Window.Resources>
    <wpfPlayground:IsAtLeastValueConverter x:Key="IsAtLeastValueConverter" />
</Window.Resources>

Edit: VM Version

I've put in elipsis (...) where I didn't make changes to your code.

<Button ... IsEnabled={Binding Path=ButtonIsEnabled} ...>


class CalcViewModel:INotifyPropertyChanged
{
    private CCalc _calc;
    private bool _buttonIsEnabled;
    public ButtonIsEnabled {
        get { return _buttonIsEnabled; }
        set { 
            _buttonIsEnabled = value;
            RaisePropertyChanged("ButtonIsEnabled");
        }
    }

    public int Counter
    {
        get
        { return _calc.Counter; }
        set{ 
            _calc.Counter = value;
            _buttonIsEnabled = _calc.Counter > 5;
        }
    }
    ...
}

So what happens here is when you change the counter value, you set the ButtonIsEnabled property which raises the property changed event and updates the button on the form with whatever logic you're using to determine if the button should be enabled.

Edit: You might need to remove that Binding=OneWay from the textbox, I'm not sure if it will initiate the set property if you're using that setting.

Upvotes: 3

BRAHIM Kamel
BRAHIM Kamel

Reputation: 13755

You should change your ViewModel to something like this

 public class  ViewModel:INotifyPropertyChanged  

        {
        public  int Counter
        {
            get { return _counter;  }
            set {
                _counter = value; 
                 RaisePropChanged("Counter");
                //for example 
                if (value>3)
                {
                    IsButtonCounterEnabled = true;  
                }
                else
                {
                    IsButtonCounterEnabled = false;  


                }
            }
        }
        public bool IsButtonCounterEnabled
        {
            get { return _IsButtonCounterEnabled;   }
            set { _IsButtonCounterEnabled = value;  
                  RaisePropChanged("IsButtonCounterEnabled");
                }
        }
        private  void RaisePropChanged(string propName)
        {
            PropertyChanged(this,new PropertyChangedEventArgs(propName));   

        }


            public event PropertyChangedEventHandler PropertyChanged = delegate{};
            private int _counter;
            private bool _IsButtonCounterEnabled;
}

and then Bind your button like this

 <Button    IsEnabled="{Binding IsButtonCounterEnabled,Mode=OneWay}" Content="Button" HorizontalAlignment="Left" Height="47" Margin="215,57,0,0" VerticalAlignment="Top" Width="159"/>

Hope this help

Upvotes: 1

Related Questions