views:

52

answers:

3

I am using MVVM pattern. I have a

  1. Text box whose Text property is bound to ViewModel's(VM supports INotifyProperyChange) Text property
  2. Button whose command is bound to VM's ICommand property type

You may think of this as a SearchTextBox and SearchButton

The problem I am facing is that when I enter the text in SearchTextBox and click on SearchButton then only the SearchTextBox bound 'set' property implementation is called but the Command for SearchButton click never executes (Note: ICommand CanExecute handler always returns True)

It works fine if I either tab out of SearchTextBox using TAB key or use mouse to move focus away from SearchTextBox and then click the SearchButton. That means do two seperate actions to trigger both the events seperately. Ideally clicking on the SearchButton should result in the SearchTextBox loose focus thus calling 'Set' property and the click on the Search button translates into the command execution.

Code is as below

<TextBox Text="{Binding Path=SearchText,Mode=TwoWay}"/>

<Button Content="Search" Width="100" Command="{Binding MySearchCommand}"/>

public String _SearchText;
public String SearchText
{
   get { return _SearchText; }
   set 
   {
     _SearchText = value;
     OnPropertyChanged("SearchText"); 
    }
 }

ICommand implementation is a standard implemenetation with no fancy code and CanExecute handler always returns True

A: 

Try to isolate the issue by writing a small test project that reproduces the issue, if you can repro then please post the code. Usually when you repro the issue outside of your main project the problem and the solution become obvious.

Sijin
That's what I am doing now!
byte
A: 

I created a sample application to reproduce this problem.

I placed breakpoint and added a Debug.Writeline in SearchText - Set property and MySearchCommandExecute method.

When breakpoints are set, only the SearchText - Set property gets called. I observed that if I remove the breakpoint from SearchText - Set property then both the property and the command are correctly executed. Looks like some problem with VS 2008 but I may be wrong.

The relevant sample code is as below

class SearchViewModel : ViewModelBase
    {
        public SearchViewModel() 
        {

        }

        public String _SearchText;
        public String SearchText
        {
            get { return _SearchText; }
            set
            {
                System.Diagnostics.Debug.WriteLine("Set Membership called");

                OnPropertyChanged("SearchText");
            }
        }

        #region Commands
        RelayCommand _SearchCommand;

        public ICommand SearchCommand
        {
            get
            {
                if (_SearchCommand == null)
                {
                    _SearchCommand = new RelayCommand(param => this.MySearchCommandExecute(), param => this.MySearchCommandCanExecute);
                }
                return _SearchCommand;
            }
        }

        public void MySearchCommandExecute()
        {
            System.Diagnostics.Debug.WriteLine("MySearchCommandExecute called");

            // Do Search
        }

        public bool MySearchCommandCanExecute
        {
            get
            {
                return true;
            }
        }

        #endregion
    }

SearchView.xaml

<UserControl x:Class="WpfApplication2.SearchView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Height="300" Width="300">
    <StackPanel>
        <StackPanel Orientation="Vertical" HorizontalAlignment="Left" Margin="4">
            <Label Foreground="Black"  FontFamily="Calibri" Width="155" Margin="4,0,4,0" Content="SearchText"/>
            <TextBox Foreground="Black"  FontFamily="Calibri" Width="155" Margin="4,0,4,0" Text="{Binding Path=SearchText}"/>
        </StackPanel>
        <Button HorizontalAlignment="Left" Content="Search" Width="100" Command="{Binding SearchCommand}" Margin="8"/>
    </StackPanel>
</UserControl>

RelayCommand.cs

// Reference: MSDN sample
    class RelayCommand : ICommand
    {
        readonly Action<object> _execute;
        readonly Predicate<object> _canExecute;

        public RelayCommand(Action<object> execute)
            : this(execute, null)
        {
        }

        public RelayCommand(Action<object> execute, Predicate<object> canExecute)
        {
            if (execute == null)
                throw new ArgumentNullException("relaycommand execute");

            _execute = execute;
            _canExecute = canExecute;
        }

        [DebuggerStepThrough]
        public bool CanExecute(object parameter)
        {
            return _canExecute == null ? true : _canExecute(parameter);
        }

        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }

        public void Execute(object parameter)
        {
            _execute(parameter);
        }
    }
byte
A: 

Byte,

Sorry for my late response, but I hope it will become handy anyway. I'm very busy lately so I couldn't debug your code (I'll try to do that when I have more time), but please try my sample code pasted below (It works perfectly for me). As you can see it's extremely simple. I used your xaml, but for Window:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        this.DataContext = new TempViewModel();
    }
}

public class TempViewModel : INotifyPropertyChanged
{
    private String _searchText;
    private ICommand _searchCommand;

    #region Commands

    protected class Search : ICommand
    {
        private TempViewModel _viewModel;

        public bool CanExecute(object parameter)
        {
            return true;
        }

        public event EventHandler CanExecuteChanged
        {
            add { }
            remove { }
        }

        public void Execute(object parameter)
        {
            //MessageBox in VM is just for demonstration
            MessageBox.Show("command executed with search string: " + this._viewModel._searchText);
        }

        public Search(TempViewModel viewModel)
        {
            this._viewModel = viewModel;
        }
    }

    #endregion //Commands

    #region INotifyPropertyChanged

    public event PropertyChangedEventHandler PropertyChanged;

    public void OnPropertyChanged(String propertyName)
    {
        if (this.PropertyChanged != null)
            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }

    #endregion //INotifyPropertyChanged

    #region Public properties

    public String SearchText
    {
        get
        {
            return this._searchText;
        }
        set
        {
            this._searchText = value;
            OnPropertyChanged("SearchText");
        }
    }

    public ICommand SearchCommand
    {
        get
        {
            return this._searchCommand;
        }
        set
        {
            this._searchCommand = value;
            OnPropertyChanged("SearchCommand");
        }
    }

    #endregion //Public properties

    public TempViewModel()
    {
        this.SearchCommand = new Search(this);
        this.SearchText = "Sample string";
    }
}

Please feel free to ask if you have any further questions.

EDIT: Ah, sorry, but I changed Command="{Binding SearchCommand}" to Command="{Binding Path=SearchCommand}"

Piotr Justyna