0

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));
        }
    }

3 Answers 3

3

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.

Sign up to request clarification or add additional context in comments.

6 Comments

@C Bauer: Thx a lot. That would be great if you could give me a hint on the VM implementation. I added the relevant parts, i hope that i didnt miss anything or have an typo. The entire class would be to much to post here.
@ck84vi There you go. I added an explanation of what it's doing differently at the bottom.
If you have any other questions feel free to post a comment up.
Yes, i do have :-). It works fine. But the problem is that i have to click on any other control in the app to get the Button visually raised again. Since i change the style if the button is enabled or disabled. The Property is set correct so i can just click anywhere else and in this moment my button gets "raised" again. So i would need some sort of an refresh for the button style once the IsEnabled gets fired for the button. Is there a way to do that?
Not sure on that one. Try removing the focusable attribute on the button.
|
2

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}}"

3 Comments

Edy: I think you are right, considering the validation i think its definitely better to make it in the VM.
As an alternative to the above, you could simply add a different ICommand for the button whose CanExecute is _calc.Counter > 3, and whose runs is ResetCounter.Execute(), cf: blogs.msdn.com/b/mikehillberg/archive/2009/03/20/… The ICommand interface intelligently disables the button if its CanExecute method returns false.
ths, looks very interesting. i will try this also
1

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.