Включить кнопку отключения на основе выбранных строк сетки данных WPF после MVVM С#

В моем приложении WPF у меня есть сетка данных

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

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

Моя кнопка xaml похожа на

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

Я создал стандартный класс RelayCommand

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

и мой класс модели представления содержит:

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

}

Но когда я запускаю приложение, я получаю следующую ошибку в строке if (SelItm.Any())

System.ArgumentNullException: значение не может быть нулевым.

Что я делаю неправильно?

CommandParameter = "{Binding SelItm, ElementName=myGrid}" Что такое Selltm?... Этого свойства нет в сетке данных.
Felix Castor 19.11.2022 15:06

Это все равно не решает проблему. Вы указываете CommandParameter на элемент myGrid. Это означает, что путь указывает не на модель представления, а на сетку данных с несуществующим путем myGrid.SelItm. Измените SelItm -> SelectedItems.

Felix Castor 19.11.2022 15:37

Вы используете string.IsNullOrEmpty(this.SelItm), чтобы проверить, имеет ли строковая переменная значение. В вашем случае SelItm очевидно NULL. Это NULL, потому что DataGrid.SelectedItem имеет тип object и не может быть приведен к string (неправильный тип предмета). Тип — это элемент данных коллекции, связанный с DataGrid.SelectedItem.

BionicCode 19.11.2022 15:40

Кроме того, привязка Button.CommandParameter неверна. Прямо сейчас это не имеет никакого эффекта, поскольку вы не ссылаетесь на объект параметра в своем коде. Но если вы захотите сослаться на него из своих обработчиков команд, вы потерпите неудачу.

BionicCode 19.11.2022 15:43

@BionicCode, использующий if (!string.IsNullOrEmpty(this.Seltm)), выполнил свою работу, кстати, относительно привязки Button.CommandParameter. Я изначально не добавлял это в свой код, но, поскольку я получил ошибку, упомянутую в моем вопросе, я попытался добавить эту часть, это тоже не сработало. В любом случае спасибо.

Tamal Banerjee 19.11.2022 16:05
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
5
84
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Есть как минимум три варианта.

Вариант 1 — программный код

Добавьте обработчик события SelectionChanged на DataGrid.

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

Событие SelectionChanged будет вызвано, когда будет выбрана полная строка. (Вы можете явно установить SelectionUnit как «Rows» в DataGrid) В противном случае используйте событие SelectedCellsChanged.

В обработчике события SelectionChanged:

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

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

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

В модели представления, упрощенной, а также предполагающей, что ваша виртуальная машина реализует 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));
}

И ваша кнопка:

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

Вариант 3 — в CanExecute

У вас есть метод в вашей модели представления, CanShowPDF(object param), который определяет для RelayCommand, может ли команда быть выполнена или нет. WPF автоматически включает/отключает кнопку на основе метода CanExecute экземпляра ICommand. Я бы добавил метод в класс RelayCommand:

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

Вызывайте метод UpdateCanExecute в вашем экземпляре RelayCommand каждый раз, когда что-то изменяется, что может повлиять на результат CanShowPDF.

Исключение

Вы спрашиваете об исключении, которое вы получаете:

System.ArgumentNullException: Value cannot be null.

Вероятно, вы вызываете метод для чего-то, что в данный момент равно null. Но трудно понять причину с данной информацией в вопросе, и я на самом деле не знаю, что такое SelItm, хотя, увидев имя, мы могли бы догадаться.

Можете ли вы показать, как сделать часть модели представления Варианта 2 для более старых версий С#, таких как С# 5.0? Также согласно вашему варианту 3 я не уверен, где и как мне вызвать метод UpdateCanExecute в моем экземпляре RelayCommand?

Tamal Banerjee 20.11.2022 02:37

Я обновил свой ответ. Кроме того, насколько я знаю, часть модели представления не зависит от версии С#. Он должен быть действителен для всех версий. Что вы не понимаете до сих пор?

user2190492 20.11.2022 16:11

Насколько мне известно, в части модели представления объявление свойств с использованием => не работает в более старой версии С#. Кроме того, у меня уже есть метод OnPropertyChanged в базовом классе модели представления (codeshare.io/r9VNXz). Кроме того, что будет CommandParameter в вашем варианте решения 2?

Tamal Banerjee 21.11.2022 05:19

Замените строку с => на get { return _selectedItemInDg; }. Части OnPropertyChanged в моем примере кода избыточны, просто напомню, что вы должны уведомлять об обновлении свойства. И параметр вашей команды может быть чем угодно.

user2190492 21.11.2022 17:59

Как преобразовать public bool IsButtonEnabled => _selectedItemInDg != null; в более старый код С#?

Tamal Banerjee 22.11.2022 10:05
public bool IsButtonEnabled { get { return _selectedItemInDg != null;}}
user2190492 22.11.2022 12:46

Другие вопросы по теме