2

I have really simple code but it does not work.

I have a command :

public class SetYesCommand : ICommand , INotifyPropertyChanged
    {
        public event EventHandler CanExecuteChanged;
        public event PropertyChangedEventHandler PropertyChanged;

        ViewModelBase view = new ViewModelBase();

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

        public void Execute(object parameter)
        {
            view.Sname = "Yes";
            MessageBox.Show("Command works");
            onpropertychanged("Sname");
        }

        private void onpropertychanged(string propertyname)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyname));
            }
        }
    }
}

and it is my base view model class :

 public class ViewModelBase : INotifyPropertyChanged
    {
        private  string _s;
        public  string Sname {
            get { return _s; }
            set { _s = value;
                onPropertyChanged("Sname");
            }

        }

        private void onPropertyChanged(string propertyname)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyname));
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
    }
}

and this is my XAML code :

<Window x:Class="Test.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:Test"
        mc:Ignorable="d"
        xmlns:viewmodel="clr-namespace:Test.ViewModel"
         xmlns:commands="clr-namespace:Test.ViewModel.Commands"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <viewmodel:ViewModelBase  x:Key="base" ></viewmodel:ViewModelBase>
        <viewmodel:Converters  x:Key="convert" ></viewmodel:Converters>
        <commands:SetYesCommand x:Key="setyes"/>
    </Window.Resources>
    <StackPanel>
        <Button Width="100" Height="30" Command="{StaticResource setyes}"/>
        <TextBox DataContext="{StaticResource base}" Text="{Binding Sname , Mode=TwoWay}"/>
        <CheckBox DataContext="{StaticResource base}" IsChecked="{Binding Sname , Mode=TwoWay , Converter={StaticResource convert}}"  />
        <TextBox />
    </StackPanel>
</Window>

As you can see simply I bound two element in my UI to a string property in my view model base class and I defiend a command for my button in UI. The command works because as you see I check it with a messagebox but the value of two other element ( textbox and checkbox) in my UI do not change.

2
  • 1
    You are creating a new instance of ViewModelBase in your command. It's is the view model that should create the command and not the other way around. Commented Dec 14, 2018 at 14:21
  • @mm8 I tried it with static properties too and it does not work in that way too Commented Dec 14, 2018 at 14:22

3 Answers 3

2

You are creating a new instance of ViewModelBase in your command. It's is the view model that should create the command and not the other way around.

An typical implementation of the ICommand interface generally accepts an Action<object> to be executed and a Predicate<object> that decides whether to execute the command:

public class SetYesCommand : ICommand
{
    public event EventHandler CanExecuteChanged;
    private readonly Action<object> _execute;
    private readonly Predicate<object> _canExecute;

    public SetYesCommand(Action<object> execute, Predicate<object> canExecute)
    {
        _execute = execute;
        _canExecute = canExecute;
    }

    public bool CanExecute(object parameter)
    {
        if (_canExecute == null)
            return true;
        return _canExecute(parameter);
    }

    public void Execute(object parameter)
    {
        if (_execute != null)
            _execute(parameter);
    }
}

The logic is then implemented in the view model:

public class ViewModelBase : INotifyPropertyChanged
{
    private string _s;
    public string Sname
    {
        get { return _s; }
        set
        {
            _s = value;
            onPropertyChanged("Sname");
        }

    }

    public ViewModelBase()
    {
        TheCommand = new SetYesCommand(OnCommandExecuted, null);
    }

    private void OnCommandExecuted(object parameter)
    {
        Sname = "Yes";
        MessageBox.Show("Command works");
    }

    public ICommand TheCommand { get; private set; }

    private void onPropertyChanged(string propertyname)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyname));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

In the view, you bind to a command property of the view model that returns an instance of your ICommand implementation:

<Button Width="100" Height="30" DataContext="{StaticResource base}" Command="{Binding TheCommand}"/>
Sign up to request clarification or add additional context in comments.

2 Comments

That is so good thank you but still I can not undestand why even when my property was static it did not work.
2

You seem rather confused regarding the relationship between command and viewmodel.

The command object should be a property of the viewmodel, not the other way around.

Start with a basic generic ICommand implementation

public class BasicCommand: ICommand
{
    private readonly Action _execute;

    public BasicCommand(Action execute)
    {
        _execute = execute;
    }

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

    public void Execute(object parameter)
    {
        _execute?.Invoke();
    }

    public event EventHandler CanExecuteChanged;
}

Than in your viewmodel, define the command as a property something like this

public class MyViewModel: ViewModelBase
{
    public MyViewModel()
    {
        DoSomethingCommand = new BasicCommamd(()=>OnDoSomething());
    }

    public ICommand DoSomethingCommand {get;}

    private void OnDoSomething()
    {
        // put whatever you want the command to do here
    }
}

You can now set the DataContext of your Window to be an instance of MyViewModel, and bind the button's command property to DoSomethingCommand.

2 Comments

Yea I am new to MVVM and I am some kind of confused, Thank you.
Take a look at my blog then for my take on WPF / MVVM - peregrinesview.uk
2

You are creating new instance of ViewModelBase inside SetYesCommand, so view.Sname = "Yes" does not change Sname property on view model bound to elements.

This is working:

<Window x:Class="Test.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:Test"
        mc:Ignorable="d"
        xmlns:viewmodel="clr-namespace:Test.ViewModel"
        xmlns:commands="clr-namespace:Test.ViewModel.Commands"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <viewmodel:ViewModelBase  x:Key="base" ></viewmodel:ViewModelBase>
        <viewmodel:Converters  x:Key="convert" ></viewmodel:Converters>
    </Window.Resources>
    <StackPanel>
        <Button Width="100" Height="30" Command="{Binding Source={StaticResource base},Path=YesCommand}"/>
        <TextBox DataContext="{StaticResource base}" Text="{Binding Source={StaticResource base},Path=Sname , Mode=TwoWay}"/>
        <CheckBox DataContext="{StaticResource base}" IsChecked="{Binding Sname , Mode=TwoWay , Converter={StaticResource convert}}"  />
        <TextBox />
    </StackPanel>
</Window>

public class ViewModelBase : INotifyPropertyChanged
    {
        private string _s;

        private Commands.SetYesCommand _yesCommand;

        public ViewModelBase()
        {
            _yesCommand = new SetYesCommand(this);
        }

        public string Sname
        {
            get { return _s; }
            set
            {
                _s = value;
                onPropertyChanged("Sname");
            }

        }

        public SetYesCommand YesCommand => _yesCommand;

        private void onPropertyChanged(string propertyname)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyname));
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
    }

public class SetYesCommand : ICommand, INotifyPropertyChanged
{
    public event EventHandler CanExecuteChanged;
    public event PropertyChangedEventHandler PropertyChanged;

    private ViewModelBase view;

    public SetYesCommand(ViewModelBase view)
    {
        this.view = view;
    }

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

    public void Execute(object parameter)
    {
        view.Sname = "Yes";
        MessageBox.Show("Command works");
        onpropertychanged("Sname");
    }

    private void onpropertychanged(string propertyname)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyname));
        }
    }
}

Comments

Your Answer

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