Click here to Skip to main content
15,867,330 members
Please Sign up or sign in to vote.
4.00/5 (1 vote)
See more:
Hello,

I'm stuck with I think is a simple problem. I want a button to be enabled/disabled depending on the content of a textbox. The button is bound to a DelegateCommand.

This is my XAML:
XML
<Window x:Class="EnableButtonTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="200" Width="225">
    <Grid>
        <TextBox Text="{Binding Path=Content}" Height="23" HorizontalAlignment="Left" Margin="68,28,0,0" Name="textBox1" VerticalAlignment="Top" Width="120" />
        <Button Command="{Binding Path=Click}" Content="Button" Height="23" HorizontalAlignment="Left" Margin="80,72,0,0" Name="button1" VerticalAlignment="Top" Width="75" />
    </Grid>
</Window>


This is my code behind:
C#
namespace EnableButtonTest
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        internal Presenter myPresenter = new Presenter();
        public MainWindow()
        {
            DataContext = myPresenter;
            InitializeComponent();
        }
    }
}


And this is my presenter:
C#
namespace EnableButtonTest
{
    public class Presenter : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        private string _Content;
        public string Content
        {
            get { return _Content; }
            set { 
                _Content = value;
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("Content"));
                }
            }
        }

        public ICommand Click { get; private set; }

        public Presenter()
        {
            Click = new DelegateCommand(ExecuteTest, CanExecuteTest);
        }
        public void ExecuteTest()
        {
        }
        public bool CanExecuteTest()
        {
            return !string.IsNullOrWhiteSpace(Content);
        }
    }
}


I can enter content in the textbox but the Presenter doesn't seem to get notified.

What am I missing or doing wrong?

Thanks for reading,

Stefan
Posted
Updated 20-Jun-12 4:49am
v2
Comments
[no name] 20-Jun-12 10:55am    
If you move focus from your textbox does your VM get notified then?
Alpman 20-Jun-12 11:02am    
I made another try where I added a button that filled the textbox with some text but to no avail.

And changing the focus doesn't help, too.
[no name] 20-Jun-12 11:05am    
Move your DataContext assignment to after InitializeComponent
Alpman 21-Jun-12 4:27am    
That doesn't help, too, thanks.
[no name] 21-Jun-12 11:35am    
Okay.... what exactly do you mean that your Presenter does not get notified? I took your exact code and put it in a project and your Presenter IS notified. So I do not know what you mean when you say it does not work.

Here is a quick fix to your code:

public class Presenter : INotifyPropertyChanged
{
  public event PropertyChangedEventHandler PropertyChanged;
  private string _Content;
  public string Content
  {
    get { return _Content; }
    set
    {
      _Content = value;
      if (PropertyChanged != null)
      {
        PropertyChanged(this, new PropertyChangedEventArgs("Content"));
      }
      Click.RaiseCanExecuteChanged();
    }
  }

  public DelegateCommand Click { get; private set; }

  public Presenter()
  {
    Click = new DelegateCommand(ExecuteTest, CanExecuteTest);
  }
  public void ExecuteTest()
  {
  }
  public bool CanExecuteTest()
  {
    return !string.IsNullOrWhiteSpace(Content);
  }
}


You need to do the RaiseCanExecuteChanged. Note that in some situations even this will not work. Now I will do a more extreme fix:

public class Presenter : INotifyPropertyChanged
{
  public event PropertyChangedEventHandler PropertyChanged;
  private string _content;
  public string Content
  {
    get { return _content; }
    set
    {
      if (_content != value)
      {
        _content = value;
        if (PropertyChanged != null)
        {
          PropertyChanged(this, new PropertyChangedEventArgs("Content"));
        }
        Click.RaiseCanExecuteChanged();
      }
    }
  }

  public DelegateCommand Click
  {
    get
    {
      return new DelegateCommand(
        () =>
        {
          /*Execute Test*/
        },
          () => !string.IsNullOrWhiteSpace(Content)
      );
    }
  }
}


This would mean that only fire the PropertyChanged an RaiseCanExecuteChanged if the value has actually changed. It also puts all the Click code in one place. Still this is not thread safe, but probably not an issue.

I would still not consider this perfect since firing RaiseCanExecuteChanged more often then you have to.
 
Share this answer
 
Comments
Alpman 22-Jun-12 13:32pm    
Thanks for your answer. I thought that the WPF will somehow do the notification without writing code like this.
Clifford Nelson 22-Jun-12 14:50pm    
Glad to be of help. Did this fix the problem. If it did, accept the solution.
Window.xaml
C#
<window x:class="CPTest.Window1" xmlns:x="#unknown">
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="200" Width="225">
    <grid>
        <textbox text="{Binding Path=Content, UpdateSourceTrigger=PropertyChanged}" height="23" horizontalalignment="Left" margin="68,28,0,0" name="textBox1" verticalalignment="Top" width="120" acceptsreturn="False" />
        <button command="{Binding Path=Click}" content="Button" height="23" horizontalalignment="Left" margin="80,72,0,0" name="button1" verticalalignment="Top" width="75" />
    </grid>
</window>


Window.cs
C#
namespace CPTest
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class Window1 : Window
    {
        internal Presenter myPresenter = new Presenter();
        public Window1()
        {
            InitializeComponent();
            DataContext = myPresenter;
        }
    }
}


Presenter.cs
C#
namespace CPTest
{
    class Presenter : INotifyPropertyChanged
    {
        #region INotifyPropertyChanged Members
        public event PropertyChangedEventHandler PropertyChanged;
        #endregion
        private string _Content;
        public string Content
        {
            get { return _Content; }
            set
            {
                _Content = value;
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("Content"));
                }
            }
        }
        public ICommand Click { get; private set; }
        public Presenter()
        {
            Click = new DelegateCommand(ExecuteTest, CanExecuteTest);
        }
        public void ExecuteTest(object obj)
        {
        }
        public bool CanExecuteTest(object obj)
        {
            return !string.IsNullOrEmpty(Content);
        }
    }
}


If your CanExecuteTest is not getting called over and over then I suspect there is something wrong with your DelegateCommand class code.

My code for DelegateCommand is included here for completeness.
C#
class DelegateCommand : ICommand
    {
        readonly Action<object> _execute;
        readonly Predicate<object> _canExecute;
        public DelegateCommand(Action<object> execute)
            : this(execute, null)
        {
        }
        public DelegateCommand(Action<object> execute, Predicate<object> canExecute)
        {
            if (execute == null)
                throw new ArgumentNullException("execute");
            _execute = execute;
            _canExecute = canExecute;
        }
        public void Execute(object parameter)
        {
            _execute(parameter);
        }
        public bool CanExecute(object parameter)
        {
            return _canExecute == null ? true : _canExecute(parameter);
        }
        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }
    }
 
Share this answer
 
v2
Comments
Alpman 22-Jun-12 17:06pm    
You made it! Sorry, I forgot to mention that I added Microsoft.Practices.Prism as a reference to use the DelegateCommand. I'll have a look at their homepage to check for a new version or other hints/advises.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900