Команды WPF ViewModel проблема CanExecute

Я испытываю некоторые трудности с Командами контекстного меню на моей Модели Представления.

Я реализую интерфейс 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:



    

        

        
            
        

    

    
        
    

    
        
    

12
задан LiamV 6 April 2010 в 20:09
поделиться

3 ответа

Чтобы завершить ответ Уилла, вот «стандартная» реализация события CanExecuteChanged :

public event EventHandler CanExecuteChanged
{
    add { CommandManager.RequerySuggested += value; }
    remove { CommandManager.RequerySuggested -= value; }
}

(из класса Джоша Смита RelayCommand )

Кстати, вам следует возможно, подумайте об использовании RelayCommand или DelegateCommand : вы быстро устанете создавать новые классы команд для каждой своей команды ViewModels ...

21
ответ дан 2 December 2019 в 05:15
поделиться

Вы должны отслеживать, когда состояние CanExecute изменилось, и запускать событие ICommand.CanExecuteChanged.

Кроме того, вы можете обнаружить, что это не всегда работает, и в этих случаях требуется вызов CommandManager.InvalidateRequerySuggested () , чтобы надрать командному менеджеру под зад.

Если вы обнаружите, что это занимает слишком много времени, ознакомьтесь с ответом на этот вопрос.

4
ответ дан 2 December 2019 в 05:15
поделиться

Спасибо за быстрые ответы. Этот подход работает, например, если вы привязываете команды к стандартной кнопке в окне (которая имеет доступ к модели представления через свой контекст данных); Показано, что 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
    }
2
ответ дан 2 December 2019 в 05:15
поделиться
Другие вопросы по тегам:

Похожие вопросы: