[SOLVED] How to enable a button only when all three textboxes are filled using ICommand in WPF?

Issue

I’m new to MVVM. I have three textboxes and a button in my view. I want that button to be enabled when all those three textboxes are filled. My view is as below:

<StackPanel Margin="1,1,1,1" Grid.Row="0">
                <!--<Label Margin="2,2,2,2" Content="ID:"/>
                <dxe:TextEdit Margin="2,2,2,2" Text="{Binding ElementName=StudentGrid, Path=SelectedItem.Id}"/>-->
                <Label Margin="2,2,2,2" Content="Name:"/>
                <dxe:TextEdit Margin="2,2,2,2" x:Name="Name" Text="{Binding Path=Name}" />
                <Label Margin="2,2,2,2" Content="Last Name:"/>
                <dxe:TextEdit Margin="2,2,2,2" x:Name="LastName" Text="{Binding Path=LastName}" />
                <Label Margin="2,2,2,2" Content="Age:"/>
                <dxe:TextEdit Margin="2,2,2,2" x:Name="Age" Text="{Binding Path=Age}" />
            </StackPanel>
            <ListView Name="StudentGrid" Grid.Row="1" Margin="1,1,1,1" ItemsSource="{Binding studentList}">
                <ListView.View>
                    <GridView>
                        <GridViewColumn Header="ID" Width="50" DisplayMemberBinding="{DXBinding Id}"/>
                        <GridViewColumn Header="Name" Width="80" DisplayMemberBinding="{DXBinding Name}"/>
                        <GridViewColumn Header="Last Name" Width="80" DisplayMemberBinding="{DXBinding LastName}"/>
                        <GridViewColumn Header="Age" Width="50" DisplayMemberBinding="{DXBinding Age}"/>
                    </GridView>
                </ListView.View>
            </ListView>
            <StackPanel Grid.Row="2" Margin="1,2,1,1">
                <dx:SimpleButton x:Name="applybtn" Content="Insert" Width="60" HorizontalAlignment="Left" Margin="5,0,0,0" Command="{Binding Path=_myCommand}"/>
            </StackPanel>

InserCommand code is:

 public class InsertCommand : ICommand
    {
        public StudentListViewModel _viewModel { get; set; }
        public InsertCommand(StudentListViewModel viewModel)
        {
            _viewModel = viewModel;
        }
        public event EventHandler CanExecuteChanged;
        public bool CanExecute(object parameter)
        {
            return true;
        }

        public void Execute(object parameter)
        {
            _viewModel.InsertStudent();
        }
    }

StudentListViewModel code is:

public class StudentListViewModel : INotifyPropertyChanged
    {
        private string _name;
        private string _lastName;
        private int _age;
        public string Name
        {
            get => _name;
            set
            {
                _name = value;
                OnPropertyChanged("Name");
            } 
        }
        public string LastName
        { get => _lastName;
            set
            { 
                _lastName = value;
                OnPropertyChanged("LastName");
            }
        }
        public int Age 
        { get => _age;
            set 
            {
                _age = value;
                OnPropertyChanged("Age");
            }
        }
        public InsertCommand _myCommand { get; set; }
        private ObservableCollection<Student> _studentList;

        public StudentListViewModel()
        {
            _myCommand = new InsertCommand(this);
            using (MyContext context = new MyContext())
            {
                _studentList = new ObservableCollection<Student>(context.Students.ToList());
            };
        }
        public void InsertStudent()
        {
                Student st = new Student()
                {
                    Name = _name,
                    LastName = _lastName,
                    Age = _age
                };
            using (var context = new MyContext())
            {
                context.Add(st);
                context.SaveChanges();
                _studentList.Add(st); //For Getting instatnt UI update
            }
        }
        public ObservableCollection<Student> studentList
        {
            get 
            {
                return _studentList;
            }
            set
            {
                _studentList = value;
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        public void OnPropertyChanged(string propertyName)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

Solution

It’s not usual to create a new class for each command.

Instead, create a generic command class with action / func parameters to specify each command’s logic.

public class RelayCommand : ICommand
{
    private readonly Action _execute;
    private readonly Func<bool> _canExecute;

    public RelayCommand(Action execute) : this(execute, () => true)
    {
    }

    public RelayCommand(Action execute, Func<bool> canExecute)
    {
        _execute = execute ?? throw new ArgumentNullException(nameof(execute));
        _canExecute = canExecute;
    }

    public bool CanExecute(object parameter) => _canExecute.Invoke();

    public void Execute(object parameter) => _execute.Invoke();

    public event EventHandler CanExecuteChanged;

    public void RaiseCanExecuteChanged()
    {
        CanExecuteChanged?.Invoke(this, EventArgs.Empty);
    }
}

Then, create an instance of this class in your ViewModel that you can bind your button to.

public class StudentListViewModel : INotifyPropertyChanged
{
    private string _name;
    private string _lastName;
    private int _age;
    public string Name
    {
        get => _name;
        set
        {
            _name = value;
            OnPropertyChanged("Name");
            InsertCommand.RaiseCanExecuteChanged();
        } 
    }

    public string LastName
    { get => _lastName;
        set
        { 
            _lastName = value;
            OnPropertyChanged("LastName");
            InsertCommand.RaiseCanExecuteChanged();
        }
    }

    public int Age 
    { get => _age;
        set 
        {
            _age = value;
            OnPropertyChanged("Age");
            InsertCommand.RaiseCanExecuteChanged();
        }
    }

    public RelayCommand InsertCommand { get; }

    public StudentListViewModel()
    {
        InsertCommand = new RelayCommand(() => InsertStudent(),
            () => !string.IsNullOrEmpty(Name) && !string.IsNullOrEmpty(LastName) && Age > 0);
        ...
    }

    public void InsertStudent()
    {
        ....
    }
}

For a cleaner way to handle command refresh, so that each property doesn’t need to care how it is used, check out my blog post.

Answered By – Peregrine

Answer Checked By – Mildred Charles (BugsFixing Admin)

Leave a Reply

Your email address will not be published. Required fields are marked *