В моем приложении 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 на элемент myGrid. Это означает, что путь указывает не на модель представления, а на сетку данных с несуществующим путем myGrid.SelItm. Измените SelItm -> SelectedItems.
Вы используете string.IsNullOrEmpty(this.SelItm), чтобы проверить, имеет ли строковая переменная значение. В вашем случае SelItm очевидно NULL. Это NULL, потому что DataGrid.SelectedItem имеет тип object и не может быть приведен к string (неправильный тип предмета). Тип — это элемент данных коллекции, связанный с DataGrid.SelectedItem.
Кроме того, привязка Button.CommandParameter неверна. Прямо сейчас это не имеет никакого эффекта, поскольку вы не ссылаетесь на объект параметра в своем коде. Но если вы захотите сослаться на него из своих обработчиков команд, вы потерпите неудачу.
@BionicCode, использующий if (!string.IsNullOrEmpty(this.Seltm)), выполнил свою работу, кстати, относительно привязки Button.CommandParameter. Я изначально не добавлял это в свой код, но, поскольку я получил ошибку, упомянутую в моем вопросе, я попытался добавить эту часть, это тоже не сработало. В любом случае спасибо.
Есть как минимум три варианта.
Вариант 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?
Я обновил свой ответ. Кроме того, насколько я знаю, часть модели представления не зависит от версии С#. Он должен быть действителен для всех версий. Что вы не понимаете до сих пор?
Насколько мне известно, в части модели представления объявление свойств с использованием => не работает в более старой версии С#. Кроме того, у меня уже есть метод OnPropertyChanged в базовом классе модели представления (codeshare.io/r9VNXz). Кроме того, что будет CommandParameter в вашем варианте решения 2?
Замените строку с => на get { return _selectedItemInDg; }. Части OnPropertyChanged в моем примере кода избыточны, просто напомню, что вы должны уведомлять об обновлении свойства. И параметр вашей команды может быть чем угодно.
Как преобразовать public bool IsButtonEnabled => _selectedItemInDg != null; в более старый код С#?
public bool IsButtonEnabled { get { return _selectedItemInDg != null;}}
CommandParameter = "{Binding SelItm, ElementName=myGrid}"
Что такое Selltm?... Этого свойства нет в сетке данных.