Я просто хотел бы знать, почему все команды в «MenuItem» запускаются, когда выбрана только одна
Это потому, что ваша реализация MultiCommandConverter
имеет недостатки:
public object Convert( object[ ] value, Type targetType,
object parameter, CultureInfo culture )
{
_value.AddRange( value );
return new RelayCommand( GetCompoundExecute( ), GetCompoundCanExecute( ) );
}
Каждый раз, когда объекту конвертера предлагается преобразовать входные привязки в новый объект ICommand
, он добавляет несколько переданных ему значений в свой внутренний список команд и затем возвращает новый объект RelayCommand()
, который простовызывает делегатов, которые ссылаются на эти команды. То есть, поскольку экземпляры делегатов, возвращаемые GetCompoundExecute()
и GetCompoundCanExecute()
, захватывают поле _value
, изменения, которые происходят позже в списке, на который ссылается это поле, отражаются в делегатах, которые были созданы ранее.
Затем,Вы создаете этот конвертер как ресурс, не указывая x:Shared=false
. Это означает, что в каждом месте, где вы использовали конвертер, используется один и тот же объект. Таким образом, ко времени разбора всего XAML у вас есть один конвертер, который объединяет все команды, используемые для всех мест, где вы использовали этот конвертер. * в ресурсе. Но ИМХО, это не очень хороший способ сделать это. Во-первых, это означает, что теперь у вас есть ловушка в вашем конвертере, и вы должны помнить, что вам нужно указывать это каждый раз, когда вы помещаете конвертер в словарь ресурсов. С другой стороны, у вас есть другая ошибка в конвертере, когда вы добавляете значения в список каждый раз, когда вызывается метод Convert()
. Это означает, что если вы попытаетесь привязать значения, которые могут время от времени обновляться, список будет становиться все длиннее и длиннее и никогда не удалит старые значения.
Это можно исправить, сбрасывая список каждыевремя вызова метода Convert()
, то есть вызова _value.Clear()
. Но я считаю, что это все еще недостаток дизайна. Конвертер должен конвертировать . Сам по себе он не должен играть роль в результате, как ваша реализация делает это.
Альтернативы, которые IMHO были бы предпочтительнее, включают:
- Написание
CompoundRelayCommand()
, включающее * 1033Функциональные возможности * и GetCompoundCanExecute()
, которые вы создаете каждый раз при каждом вызове метода Convert()
. - Объедините входные значения в самом методе
Convert()
, создав новый многоадресный делегат, который объединяет цепочкивведите команды, а затем передайте этот многоадресный делегат RelayCommand()
.
. Любой из этих подходов будет гораздо лучше, чем просто обновить то, что у вас есть, исправив ошибку «список не очищен». и используя x:Shared="false"
в словаре ресурсов.