Я добавляю новую кнопку с функциональностью в большое существующее клиентское приложение WPF. Кнопка Command
должна запускать метод модели представления через DelegateCommand<T>
. Проблема в том, что все приложение вылетает при запуске, видимо из-за этого DelegateCommand<T>
. Мой код выглядит следующим образом.
public DelegateCommand<DocumentType> AddFileCommand { get; private set; }
public BaseFormViewModel(...)
{
this.AddFileCommand = new DelegateCommand<DocumentType>(this.ExecuteAddFileCommand);
}
protected virtual void ExecuteAddFileCommand(DocumentType documentType)
{
// Do something...
}
<ctrl:ErrorDecorator Grid.Row = "1" Grid.Column = "0">
<ComboBox AutomationProperties.AutomationId = "cmbDocumentType"
ItemsSource = "{Binding ViewModel.DocumentTypes, ElementName=Root}"
DisplayMemberPath = "Name"
SelectedValuePath = "Value"
SelectedValue = "{Binding DocumentType}"/>
</ctrl:ErrorDecorator>
<Button Grid.Row = "1" Grid.Column = "2" Content = "Tilføj fil"
Command = "{Binding ViewModel.AddFileCommand, ElementName=Root}"
CommandParameter = "{Binding DocumentType}"/>
Теперь приложение падает при запуске, и интересная часть трассировки стека выглядит следующим образом (я анонимизировал имена клиентов и термины). Извините за маленький размер изображения.
Я новичок как в WPF, так и в MVVM, поэтому для меня это что-то вроде кривой обучения. Но, насколько я понимаю, он жалуется на второй параметр DelegateCommand<T>
, который является методом CanExecute
. Но у DelegateCommand<T>
также есть конструктор, который принимает только один параметр — метод Execute
. Так почему же он жалуется на это?
Я также пытался передать метод CanExecute
, который просто возвращает true. Но приложение все равно вылетает с той же ошибкой.
Такое же использование DelegateCommand<T>
существует в нескольких местах приложения с одинаковым синтаксисом и подписью без проблем. Так что это действительно не должно быть проблемой.
Я также пробовал использовать ICommand
и ExecuteAddFileCommand
без параметров. Это работает для меня, но, очевидно, не является решением, так как мне нужно передать DocumentType
.
Может кто-нибудь, пожалуйста, помогите мне продвинуться дальше к решению?
Вы по-прежнему получаете исключение, если измените тип на DelegateCommand<object>
?
@mm8 Но тогда код, вероятно, не скомпилируется, потому что тип делегата не будет совпадать.
@mm8 На самом деле нет .. Я изменил тип на объект, и он работает. По крайней мере, приложение не вылетает. Но теперь параметр ExecuteAddFileCommand() равен нулю. Несмотря на то, что я выбрал значение в поле со списком, которое затем должно быть привязано к свойству DocumentType в модели представления.
Итак, что такое DocumentType
, к которому вы пытаетесь привязаться? Действительно ли это свойство возвращает DocumentType
?
Дайте угадаю, DocumentType
— это тип значения (вероятно, перечисление)? В этом случае, если ваша команда получает нулевой параметр, приведение выдаст это исключение.
@EtiennedeMartel да DocumentType - это перечисление.
Хорошо, тогда это очевидно: привязка CommandParameter
оценивается как null, а DelegateCommand<T>
пытается привести свой параметр к T
. Приведение null к типу значения вызовет исключение.
@EtiennedeMartel Хорошо, так что вы имеете в виду, что мое свойство DocumentType
, к которому привязывается CommandParameter
, не получает выбранное значение из поля со списком и, таким образом, передает значение null в DelegateCommand
?
@RonRonDK: Кажется вероятным. Вы читали мой ответ?
Если вы просто создадите экземпляр модели представления, произойдет ли ошибка?
Я подозреваю, что параметр команды — это нечто иное, чем объект DocumentType
.
Если вы измените тип команды на DelegateCommand<object>
, вы должны избежать получения исключения.
Затем вы можете поставить точку останова в методе ExecuteAddFileCommand
, чтобы определить фактический тип параметра и установлен ли он вообще. Если нет, проверьте исходное свойство DocumentType
, к которому вы привязываете CommandParameter
:
CommandParameter = "{Binding DocumentType}"
Его тип должен соответствовать аргументу типа DelegateCommand<T>
.
Если бы это было «что-то еще», вы бы получили InvalidCastException
. Поведение предполагает, что параметр имеет значение null
, а DocumentType
не является ссылочным типом.
null
явно «нечто иное», чем фактическое значение какого-то типа, не так ли?
Под этим я подразумеваю, что значение null имеет некоторые интересные последствия, в отличие от передачи несвязанного типа. Это значение по умолчанию? Резервное значение? Привязка не работает? Во всяком случае, это сужает пространство возможностей.
@EtiennedeMartel: Какую часть ответа вы считаете неправильной?
Ничего страшного, я просто добавил к этому, не нужно так защищаться.
В порядке. Понятно. Спасибо.
Я не понимаю, почему тип должен быть object
, чтобы избежать исключения. Это потому, что параметр команды не имеет типа DocumentType
?
object
работает для (почти) каждого типа. Вот почему я предложил вам попробовать с этим. После того, как вы разобрались, вы должны изменить фактический тип, который, по-видимому, в вашем случае Nullable<DocumentType>
.
Поэтому сегодня я углубился в это, следуя теории @mm8 о том, что мой CommandParameter
не был типом DocumentType
. Поэтому я проследил за DocumentType
до конца и обнаружил, что в некоторых местах он обнуляемый, а в других нет. И как только я сменил CommandParameter
на тип DocumentType?
, приложение перестало падать. Мой входящий параметр в ExecuteAddFileCommand()
все еще был null
. И это было, конечно, потому, что я должен был включить параметр в DelegateCommand<T>
. Итак, рабочий код выглядит так.
public DelegateCommand<DocumentType?> AddFileCommand { get; private set; }
public BaseFormViewModel(...)
{
this.AddFileCommand = new DelegateCommand<DocumentType?>(param => this.ExecuteAddFileCommand(param));
}
protected virtual void ExecuteAddFileCommand(DocumentType? documentType)
{
// Do something...
}
Так почему вы не приняли или не проголосовали за мой ответ?
@mm8 Ваш ответ помог мне продвинуться дальше, но не стал прямым решением проблемы. Так что я на самом деле не уверен, принимать ли это как ответ в соответствии с правилами SO?
Что заставляет вас говорить, что изменение аргумента типа не было «прямым решением проблемы»...?
@ mm8 Ты прав .. Я принял твой ответ. Спасибо за вашу помощь.
Трудно сказать, не глядя на код
DelegateCommand<T>
, правда.