Search code examples
c#wpfmvvmrelaycommandcanexecute

Toggle "CanExecute" of a button based on grid selection


I am very to Modern UI Programming and now i got stuck in a small C# WPF Application which is basically a learning project of MVVM design pattern.

I have a DataGrid and some Buttons to handle data operation (add, edit, delete).

What i want to achieve: the edit button should not be enable, when no row in grid is selected.

edit button:

<Button Width="126" Height="22" Content="Edit" Margin="5,5,5,5" Command="{Binding KontoEdit}" />

grid:

   <DataGrid ItemsSource="{Binding Konten}"  SelectedItem="{Binding SelectedKonto}">
        <DataGrid.Columns>
            <DataGridTextColumn Binding="{Binding Path=KtoNr}" Header="Nr" IsReadOnly="True" />
            <DataGridTextColumn Binding="{Binding Path=Name}" Header="Name" IsReadOnly="True"  />
            <DataGridTextColumn Binding="{Binding Path=KtoArt}" Header="Kontoart" IsReadOnly="True"  />
            <DataGridTextColumn Binding="{Binding Path=KtoKlasse}" Header="Kontenklasse" IsReadOnly="True"  />
        </DataGrid.Columns>
    </DataGrid>

view model:

public class MainViewModel : INotifyPropertyChanged
{
    KontenDB ctx = new KontenDB();

    public MainViewModel()
    {
        FillKonten();

        CanKontoEditExecute = true ;

        KontoEdit = new RelayCommand(o => { DoKontoEdit(SelectedKonto); }, param => CanKontoEditExecute);
    }


    #region //Commands
    public void DoKontoEdit(Konten k)
    {
        //Edit the Selected Item
    }

    private ICommand _kontoEdit;
    public ICommand KontoEdit
    {
        get
        {
            return _kontoEdit;
        }
        set
        {
            _kontoEdit = value;
        }
    }

    private bool _canKontoEditExecute;

    public bool CanKontoEditExecute
    {
        get
        {
            return _canKontoEditExecute;
        }
        set
        {
            _canKontoEditExecute = value;
        }
    }

    #endregion //Commands
    private void FillKonten()
    {
        var q = (from k in ctx.Konten
                 select k).ToList();
        Konten = new ObservableCollection<Konten>(q);
    }


    private ObservableCollection<Konten> _konten;
    public ObservableCollection<Konten> Konten
    {
        get
        {
            return _konten;
        }
        set
        {
            _konten = value;
            NotifyPropertyChanged();
        }
    }

    private Konten _selectedKonto;
    public Konten SelectedKonto
    {
        get
        {
            return _selectedKonto;
        }
        set
        {
            _selectedKonto = value;
            NotifyPropertyChanged();
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

model is generated by EF6.

edit: RelayCommand class:

public class RelayCommand : ICommand
    {
        private Action<object> execute;

        private Predicate<object> canExecute;

        private event EventHandler CanExecuteChangedInternal;

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

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

            if (canExecute == null)
            {
                throw new ArgumentNullException("canExecute");
            }

            this.execute = execute;
            this.canExecute = canExecute;
        }

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

            remove
            {
                CommandManager.RequerySuggested -= value;
                CanExecuteChangedInternal -= value;
            }
        }

        public bool CanExecute(object parameter)
        {
            return canExecute != null && canExecute(parameter);
        }

        public void Execute(object parameter)
        {
            execute(parameter);
        }

        public void OnCanExecuteChanged()
        {
            EventHandler handler = CanExecuteChangedInternal;
            if (handler != null)
            {
                handler.Invoke(this, EventArgs.Empty);
            }
        }

        public void Destroy()
        {
            canExecute = _ => false;
            execute = _ => { return; };
        }

        private static bool DefaultCanExecute(object parameter)
        {
            return true;
        }
    }

So how can i achieve that when: no row in datagrid is selected or SelectedKonto is null the CanKontoEditExecute property changes to false?

Thanks a lot for your help!


Solution

  • Bring in following corrections

    SelectedItem="{Binding SelectedKonto, Mode=TwoWay}"
    


    KontoEdit = new RelayCommand(o => { DoKontoEdit(SelectedKonto); }, param => SelectedKonto != nulll);
    


    private object _selectedItem;
    public object SelectedItem
    {
        get { return _selectedItem; }
        set
        {
            _selectedItem = value;
            PropertyChanged(this,new PropertyChangedEventArgs("SelectedItem"));
        }
    }