Переключение кнопки "CanExecute" на основе выбора сетки

Я очень люблю современное программирование пользовательского интерфейса, и теперь я застрял в небольшом WPF-приложении на C#, которое в основном представляет собой учебный проект шаблона проектирования MVVM.

У меня есть DataGrid и несколько кнопок для обработки данных (добавить, изменить, удалить).

Чего я хочу добиться: кнопка редактирования не должна быть включена, когда ни одна строка в сетке не выбрана.

кнопка редактирования:

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

сетка:

   <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>

посмотреть модель:

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

Модель сгенерирована EF6.

редактировать: класс RelayCommand:

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

Итак, как я могу добиться этого, когда: ни одна строка в сетке данных не выбрана или SelectedKonto имеет значение null, свойство CanKontoEditExecute изменяется на false?

Большое спасибо за вашу помощь!

2 ответа

Решение

Внести следующие исправления

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

Вы должны определить свойство SelectedItem в вашей ViewModel и связать его с вашей DataGrid.

CS:

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

            DoKontoEdit.OnCanExecuteChanged();
        }
    }

XAML:

    <DataGrid SelectedItem="{Binding SelectedItem}">
        ....

Тогда ваша команда должна быть инициализирована так:

   new RelayCommand(o => { DoKontoEdit(SelectedKonto); }, _ => SelectedItem != null);

Вот что я считаю лучшей реализацией ICommand. Мне больше нравится, когда я решаю, когда оценивать делегат canExecute и удалять оцениваемый RequireySuggested из CommandManager. Вы можете попробовать это.

         public class RelayCommand<T> : IRelayCommand
{
    private Predicate<T> _canExecute;
    private Action<T> _execute;

    public RelayCommand(Action<T> execute, Predicate<T> canExecute = null)
    {
        _execute = execute;
        _canExecute = canExecute;
    }

    public void Execute(T parameter)
    {
        _execute(parameter);
    }

    private bool CanExecute(T parameter)
    {
        return _canExecute == null ? true : _canExecute(parameter);
    }

    public virtual bool CanExecute(object parameter)
    {
        return parameter == null ? false : CanExecute((T)parameter);
    }

    public void Execute(object parameter)
    {
        _execute((T)parameter);
    }

    public event EventHandler CanExecuteChanged;

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


    public void RaiseCanExecuteChanged()
    {
        var temp = Volatile.Read(ref CanExecuteChanged);

        if (temp != null)
            temp(this, new EventArgs());
    }
}

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

    public RelayCommand(Action execute , Func<bool> canExecute = null)
    {
        _execute = execute;
        _canExecute = canExecute;
    }

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

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

    public event EventHandler CanExecuteChanged;

    public void RaiseCanExecuteChanged()
    {
        var temp = Volatile.Read(ref CanExecuteChanged);

        if (temp != null)
            temp(this, new EventArgs());
    }

    public void Execute(object parameter)
    {
        _execute();
    }
}
Другие вопросы по тегам