Я испытываю некоторые трудности с Командами контекстного меню на моей Модели Представления.
Я реализую интерфейс ICommand для каждой команды в рамках Модели Представления, затем создавая ContextMenu в ресурсах Представления (MainWindow), и с помощью CommandReference от MVVMToolkit для доступа к текущему DataContext (ViewModel) Команды.
Когда я отлаживаю приложение, кажется, что кроме метода CanExecute на команде не называют при создании окна, поэтому мой Контекст MenuItems не включают или отключают, как я ожидал бы.
Я приготовил простую выборку (присоединенный здесь), который показателен из моего реального приложения и полученный в итоге ниже. Любая справка значительно ценилась бы!
Это - ViewModel
namespace WpfCommandTest
{
public class MainWindowViewModel
{
private List data = new List{ "One", "Two", "Three" };
// This is to simplify this example - normally we would link to
// Domain Model properties
public List TestData
{
get { return data; }
set { data = value; }
}
// Bound Property for listview
public string SelectedItem { get; set; }
// Command to execute
public ICommand DisplayValue { get; private set; }
public MainWindowViewModel()
{
DisplayValue = new DisplayValueCommand(this);
}
}
}
DisplayValueCommand - такой:
public class DisplayValueCommand : ICommand
{
private MainWindowViewModel viewModel;
public DisplayValueCommand(MainWindowViewModel viewModel)
{
this.viewModel = viewModel;
}
#region ICommand Members
public bool CanExecute(object parameter)
{
if (viewModel.SelectedItem != null)
{
return viewModel.SelectedItem.Length == 3;
}
else return false;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
MessageBox.Show(viewModel.SelectedItem);
}
#endregion
}
И наконец, представление определяется в Xaml:
Чтобы завершить ответ Уилла, вот «стандартная» реализация события CanExecuteChanged
:
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
(из класса Джоша Смита RelayCommand
)
Кстати, вам следует возможно, подумайте об использовании RelayCommand
или DelegateCommand
: вы быстро устанете создавать новые классы команд для каждой своей команды ViewModels ...
Вы должны отслеживать, когда состояние CanExecute изменилось, и запускать событие ICommand.CanExecuteChanged.
Кроме того, вы можете обнаружить, что это не всегда работает, и в этих случаях требуется вызов CommandManager.InvalidateRequerySuggested ()
, чтобы надрать командному менеджеру под зад.
Если вы обнаружите, что это занимает слишком много времени, ознакомьтесь с ответом на этот вопрос.
Спасибо за быстрые ответы. Этот подход работает, например, если вы привязываете команды к стандартной кнопке в окне (которая имеет доступ к модели представления через свой контекст данных); Показано, что CanExecute вызывается довольно часто при использовании CommandManager, как вы предлагаете в классах реализации ICommand, или при использовании RelayCommand и DelegateCommand.
Однако привязка тех же команд через CommandReference в ContextMenu не действует одинаково.
Чтобы обеспечить такое же поведение, я должен также включить EventHandler из RelayCommand Джоша Смита в CommandReference, но при этом я должен закомментировать некоторый код из метода OnCommandChanged. Я не совсем уверен, почему это происходит, возможно, это предотвращает утечку памяти событий (предположительно!)?
public class CommandReference : Freezable, ICommand
{
public CommandReference()
{
// Blank
}
public static readonly DependencyProperty CommandProperty = DependencyProperty.Register("Command", typeof(ICommand), typeof(CommandReference), new PropertyMetadata(new PropertyChangedCallback(OnCommandChanged)));
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
#region ICommand Members
public bool CanExecute(object parameter)
{
if (Command != null)
return Command.CanExecute(parameter);
return false;
}
public void Execute(object parameter)
{
Command.Execute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
private static void OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
CommandReference commandReference = d as CommandReference;
ICommand oldCommand = e.OldValue as ICommand;
ICommand newCommand = e.NewValue as ICommand;
//if (oldCommand != null)
//{
// oldCommand.CanExecuteChanged -= commandReference.CanExecuteChanged;
//}
//if (newCommand != null)
//{
// newCommand.CanExecuteChanged += commandReference.CanExecuteChanged;
//}
}
#endregion
#region Freezable
protected override Freezable CreateInstanceCore()
{
throw new NotImplementedException();
}
#endregion
}