Home > Enterprise >  Enable disable button based on WPF datagrid selected rows following MVVM c#
Enable disable button based on WPF datagrid selected rows following MVVM c#

Time:11-22

In my WPF app I have a datagrid

<DataGrid SelectedItem="{Binding SelItm ,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
          ItemsSource="{Binding Bills}"
          SelectionMode="Extended"
          Name="myGrid"...

Now I want to enable a button if any rows of the datagrid are selected, if no rows are selected the button should be disabled, pretty simple stuff.

My button xaml is like

<Button
    Command="{Binding PreviewButtonClicked}"
    CommandParameter="{Binding SelItm, ElementName=myGrid}"

I've create a standard RelayCommand class

public class RelayCommand : ICommand
    {
        private readonly Action<object> _execute;
        private 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("execute");
            _execute = execute;
            _canExecute = canExecute;
        }

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

and my viewmodel class contains:

public class myVM()
{
    private string _SelItm;
    public string SelItm
    {
        get { return _SelItm; }
        set
        {
            SetValue(ref _SelItm, value);
        }
    }
  public RelayCommand PreviewButtonClicked { get; set; }

  public myVM()
  {
    PreviewButtonClicked = new RelayCommand(ShowPDF, CanShowPDF);
  }
  
  public void ShowPDF(object param)
  {
    //do stuff
  }

  public bool CanShowPDF(object param)
  {
        if (SelItm.Any())
        {
            return true;
        }
            
        return false;
  }

}

But when I run the app I get the below error on line if (SelItm.Any())

System.ArgumentNullException: Value cannot be null.

What am I doing incorrectly ?

CodePudding user response:

There are at least three options.

Option 1 - Codebehind

Add an event handler to the SelectionChanged event on the DataGrid.

<DataGrid SelectedItem="{Binding SelItm ,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
          ItemsSource="{Binding Bills}"
          SelectionMode="Extended"
          SelectionChanged="DataGridSelectionChanged"
          Name="myGrid"...

The SelectionChanged event will be invoked when there's a full row selected. (You can set the SelectionUnit as "Rows" on the DataGrid explicitly) Otherwise use the SelectedCellsChanged event.

In the handler of the SelectionChanged event:

private void DataGridSelectionChanged(object sender, SelectionChangedEventArgs args) 
{
    myButton.IsEnabled = myGrid.SelectedItems.Count > 0;
}

Option 2 You can bind the SelectedItem property on the DataGrid, to your viewmodel.

<DataGrid SelectedItem="{Binding SelItm ,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
          ItemsSource="{Binding Bills}"
          SelectionMode="Extended"
          SelectedItem="{Binding SelectedItemInDataGrid}"
          Name="myGrid"...

In the viewmodel, simplified and also assuming your vm implements INotifyPropertyChanged:

private object _selectedItemInDg;

public object SelectedItemInDataGrid 
{
    set { 
        _selectedItemInDg = value; 
        OnPropertyChanged(nameof(SelectedItemInDataGrid ));
        OnPropertyChanged(nameof(IsButtonEnabled)); 
        PreviewButtonClicked?.UpdateCanExecute();
    }
    get => _selectedItemInDg;
}

public bool IsButtonEnabled => _selectedItemInDg != null;

private void OnPropertyChanged(string name) 
{
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}

And your button:

<Button IsEnabled="{Binding IsButtonEnabled}" Command="..." CommandParameter="...">Content of button</Button>

Option 3 - In CanExecute

You've got a method in your viewmodel, CanShowPDF(object param), which determines for the RelayCommand wether the command can be executed or not. WPF enables/disables the button automatically based on the CanExecute method of an ICommand instance. I'd add a method to the RelayCommand class:

public void UpdateCanExecute() {
    CanExecuteChanged?.Invoke();
}

Call the UpdateCanExecute method on your RelayCommand instance every time something changes that would affect the result of CanShowPDF.

The exception

You ask about an exception you get:

System.ArgumentNullException: Value cannot be null.

Probably you are calling a method on something that is null at that moment. But it's hard to figure out the cause with the given information in the question and I don't actually know what SelItm is, although seen the name we could guess.

  • Related