WPF CommandParameter имеет значение NULL при первом вызове CanExecute

У меня возникла проблема с WPF и командами, привязанными к кнопке внутри DataTemplate элемента управления ItemsControl. Сценарий довольно прост. ItemsControl привязан к списку объектов, и я хочу иметь возможность удалить каждый объект в списке, нажав кнопку. Кнопка выполняет команду, а команда заботится об удалении. CommandParameter привязан к объекту, который я хочу удалить. Таким образом, я знаю, что щелкнул пользователь. Пользователь должен иметь возможность удалять только свои «собственные» объекты, поэтому мне нужно выполнить некоторые проверки в вызове «CanExecute» команды, чтобы убедиться, что у пользователя есть необходимые разрешения.

Проблема в том, что параметр, переданный в CanExecute, имеет значение NULL при первом вызове, поэтому я не могу запустить логику для включения / отключения команды. Однако, если я сделаю это всегда включенным, а затем нажму кнопку, чтобы выполнить команду, CommandParameter будет передан правильно. Это означает, что привязка к CommandParameter работает.

XAML для ItemsControl и DataTemplate выглядит следующим образом:

<ItemsControl 
    x:Name="commentsList"
    ItemsSource="{Binding Path=SharedDataItemPM.Comments}"
    Width="Auto" Height="Auto">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <Button                             
                    Content="Delete"
                    FontSize="10"
                    Command="{Binding Path=DataContext.DeleteCommentCommand, ElementName=commentsList}" 
                    CommandParameter="{Binding}" />
            </StackPanel>                       
         </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

Как видите, у меня есть список объектов комментариев. Я хочу, чтобы CommandParameter DeleteCommentCommand был привязан к объекту Command.

Итак, я предполагаю, что мой вопрос: кто-нибудь сталкивался с этой проблемой раньше? CanExecute вызывается в моей команде, но в первый раз параметр всегда имеет значение NULL - почему?

Обновление: мне удалось немного сузить проблему. Я добавил пустой Debug ValueConverter, чтобы я мог выводить сообщение, когда CommandParameter привязан к данным. Оказывается, проблема в том, что метод CanExecute выполняется до того, как CommandParameter привязан к кнопке. Я попытался установить CommandParameter перед командой (как предлагалось), но это все еще не работает. Любые советы о том, как это контролировать.

Обновление2: Есть ли способ определить, когда привязка завершена, чтобы я мог принудительно выполнить повторную оценку команды? Кроме того, проблема в том, что у меня есть несколько кнопок (по одной для каждого элемента в ItemsControl), которые привязаны к одному и тому же экземпляру объекта Command?

Update3: Я загрузил репродукцию ошибки на свой SkyDrive: http://cid-1a08c11c407c0d8e.skydrive.live.com/self.aspx/Code%20samples/CommandParameterBinding.zip


person Jonas Follesø    schedule 02.12.2008    source источник
comment
У меня такая же проблема с ListBox.   -  person Hadi Eskandari    schedule 02.10.2015
comment
В настоящее время существует открытый отчет об этой проблеме в WPF: github.com/dotnet/wpf/issues / 316   -  person StayOnTarget    schedule 15.05.2020


Ответы (15)


Я наткнулся на похожую проблему и решил ее с помощью моего надежного TriggerConverter.

public class TriggerConverter : IMultiValueConverter
{
    #region IMultiValueConverter Members

    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        // First value is target value.
        // All others are update triggers only.
        if (values.Length < 1) return Binding.DoNothing;
        return values[0];
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    #endregion
}

Этот преобразователь значений принимает любое количество параметров и возвращает первый из них как преобразованное значение. При использовании в MultiBinding в вашем случае это выглядит следующим образом.

<ItemsControl 
    x:Name="commentsList"
    ItemsSource="{Binding Path=SharedDataItemPM.Comments}"
    Width="Auto" Height="Auto">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <Button                             
                    Content="Delete"
                    FontSize="10"
                    CommandParameter="{Binding}">
                    <Button.Command>
                        <MultiBinding Converter="{StaticResource TriggerConverter}">
                            <Binding Path="DataContext.DeleteCommentCommand"
                                     ElementName="commentsList" />
                            <Binding />
                        </MultiBinding> 
                    </Button.Command>
                </Button>
            </StackPanel>                                       
         </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

Вам нужно будет добавить TriggerConverter в качестве ресурса, чтобы это работало. Теперь свойство Command устанавливается не раньше, чем становится доступным значение CommandParameter. Вы даже можете выполнить привязку к RelativeSource.Self и CommandParameter вместо. для достижения того же эффекта.

person David Liersch    schedule 05.02.2009
comment
Это сработало для меня. Я не понимаю почему. Кто-нибудь может объяснить? - person T.J.Kjaer; 06.03.2013
comment
Разве это не работает, потому что CommandParameter привязан до Command? Сомневаюсь, что вам понадобится конвертер ... - person MBoros; 07.04.2014
comment
Это не выход. Это взлом? Что, черт возьми, происходит? Это работало? - person Jordan; 15.09.2015
comment
Отлично, у меня работает! Магия заключается в строке ‹Binding /›, которая заставляет привязку команды обновляться при изменении шаблона данных (который привязан к параметру команды) - person Andreas Kahler; 17.01.2017

У меня возникла такая же проблема при попытке привязки к команде в моей модели представления.

Я изменил его, чтобы использовать относительную привязку источника, а не ссылаться на элемент по имени, и это помогло. Привязка параметров не изменилась.

Старый код:

Command="{Binding DataContext.MyCommand, ElementName=myWindow}"

Новый код:

Command="{Binding DataContext.MyCommand, RelativeSource={RelativeSource AncestorType=Views:MyView}}"

Обновление: я только что столкнулся с этой проблемой, не используя ElementName, я привязываюсь к команде в моей модели представления, и мой контекст данных кнопки - это моя модель представления. В этом случае мне пришлось просто переместить атрибут CommandParameter перед атрибутом Command в объявлении кнопки (в XAML).

CommandParameter="{Binding Groups}"
Command="{Binding StartCommand}"
person Travis Weber    schedule 28.10.2010
comment
Перемещение CommandParameter перед Command - лучший ответ в этой теме. - person BSick7; 25.02.2011
comment
Изменение порядка атрибутов нам не помогло. Я был бы удивлен, если бы это повлияло на порядок исполнения. - person Jack Ukleja; 11.10.2011
comment
Не знаю, почему это работает. Кажется, что этого не должно быть, но так оно и есть. - person RMK; 17.01.2012
comment
Решение RelativeSource отлично сработало для меня и не требовало ненужного конвертера. RelativeSource тоже чувствует себя лучше, поскольку он не связан произвольно с текстовой строкой. Он ищет в дереве элемент над ним, который имеет нужное значение. - person Bryan Walker; 25.01.2012
comment
У меня была такая же проблема - RelativeSource не помог, изменение порядка атрибутов помогло. Спасибо за обновления! - person Grant Crofton; 26.04.2012
comment
Как человека, который неукоснительно использует расширения для автоматического украшения XAML (разделение атрибутов по строкам, исправление отступов, изменение порядка атрибутов), предложение об изменении порядка CommandParameter и Command пугает меня. - person Guttsy; 24.04.2014
comment
Идеально! Работал у меня. Большое спасибо! (Я использую Prism и DelegeteCommand ‹T›) - person Vladislav; 27.01.2016
comment
@JackUkleja Конечно, последовательность атрибутов имеет значение: угадайте, в какой последовательности инициализируются свойства зависимости ... - person oddparity; 17.02.2020
comment
У меня была такая же проблема, когда ElementName не был уникальным (из-за копирования / вставки). Изменение на RelativeSource или переименование элемента решило проблему. - person Dirk Vollmar; 25.11.2020

Я обнаружил, что порядок, в котором я устанавливаю Command и CommandParameter, имеет значение. Установка свойства Command вызывает немедленный вызов CanExecute, поэтому вы хотите, чтобы CommandParameter уже был установлен в этот момент.

Я обнаружил, что изменение порядка свойств в XAML действительно может иметь эффект, хотя я не уверен, что это решит вашу проблему. Однако попробовать стоит.

Кажется, вы предполагаете, что кнопка никогда не становится активной, что удивительно, поскольку я ожидал, что CommandParameter будет установлен вскоре после свойства Command в вашем примере. Вызывает ли вызов CommandManager.InvalidateRequerySuggested () включение кнопки?

person Ed Ball    schedule 03.12.2008
comment
Пытался установить CommandParameter перед командой - по-прежнему выполняется CanExecute, но по-прежнему передается NULL ... Облом - но спасибо за подсказку. Кроме того, вызов CommandManager.InvalidateRequerySuggested (); не имеет значения. - person Jonas Follesø; 03.12.2008
comment
CommandManager.InvalidateRequerySuggested () решил аналогичную проблему для меня. Спасибо! - person MJS; 22.12.2008

Я придумал другой вариант решения этой проблемы, которым я хотел бы поделиться. Поскольку метод CanExecute команды выполняется до того, как будет установлено свойство CommandParameter, я создал вспомогательный класс с присоединенным свойством, которое заставляет метод CanExecute вызываться снова при изменении привязки.

public static class ButtonHelper
{
    public static DependencyProperty CommandParameterProperty = DependencyProperty.RegisterAttached(
        "CommandParameter",
        typeof(object),
        typeof(ButtonHelper),
        new PropertyMetadata(CommandParameter_Changed));

    private static void CommandParameter_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var target = d as ButtonBase;
        if (target == null)
            return;

        target.CommandParameter = e.NewValue;
        var temp = target.Command;
        // Have to set it to null first or CanExecute won't be called.
        target.Command = null;
        target.Command = temp;
    }

    public static object GetCommandParameter(ButtonBase target)
    {
        return target.GetValue(CommandParameterProperty);
    }

    public static void SetCommandParameter(ButtonBase target, object value)
    {
        target.SetValue(CommandParameterProperty, value);
    }
}

А затем на кнопке, к которой вы хотите привязать параметр команды ...

<Button 
    Content="Press Me"
    Command="{Binding}" 
    helpers:ButtonHelper.CommandParameter="{Binding MyParameter}" />

Я надеюсь, что это, возможно, поможет кому-то еще с проблемой.

person Ed Downs    schedule 10.07.2014
comment
Хорошо сделано, спасибо. Я не могу поверить, что M $ не исправил это за 8 лет. Ужасно! - person McGarnagle; 06.02.2016

Это старый поток, но поскольку Google привел меня сюда, когда у меня возникла эта проблема, я добавлю то, что у меня сработало для DataGridTemplateColumn с помощью кнопки.

Измените привязку с:

CommandParameter="{Binding .}"

to

CommandParameter="{Binding DataContext, RelativeSource={RelativeSource Self}}"

Не знаю, почему это работает, но для меня это сработало.

person Simon Smith    schedule 15.08.2016
comment
Я пробовал оба ответа с высокими оценками выше, но этот сработал только для меня. Кажется, что это внутренняя проблема управления, а не привязка, но все же многие люди работают с приведенными выше ответами. Спасибо! - person Javidan; 14.12.2017
comment
Невероятно работает! - person WAKU; 11.03.2021

Недавно я столкнулся с той же проблемой (для меня это было для пунктов меню в контекстном меню), но, хотя это может быть не подходящее решение для каждой ситуации, я нашел другой (и намного более короткий!) Способ решения этой проблемы. проблема:

<MenuItem Header="Open file" Command="{Binding Tag.CommandOpenFile, IsAsync=True, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}" CommandParameter="{Binding Name}" />

Игнорируя обходной путь на основе Tag для особого случая контекстного меню, главное здесь - регулярно связывать CommandParameter, но связывать Command с дополнительным IsAsync=True. Это немного задержит привязку фактической команды (и, следовательно, ее вызов CanExecute), поэтому параметр уже будет доступен. Однако это означает, что на короткое время состояние включения могло быть неправильным, но для моего случая это было вполне приемлемо.

person Ralf Stauder    schedule 10.05.2019

Возможно, вы сможете использовать мой CommandParameterBehavior, который я разместил на форумах Prism вчера. Он добавляет недостающее поведение, когда изменение CommandParameter вызывает повторный запрос Command.

Здесь есть некоторая сложность, вызванная моими попытками избежать утечки памяти, вызванной вызовом PropertyDescriptor.AddValueChanged без последующего вызова PropertyDescriptor.RemoveValueChanged. Я пытаюсь исправить это, отменяя регистрацию обработчика, когда ekement выгружается.

Вам, вероятно, потребуется удалить IDelegateCommand материал, если вы не используете Prism (и хотите внести те же изменения, что и я, в библиотеку Prism). Также обратите внимание, что мы обычно здесь не используем RoutedCommand (мы используем DelegateCommand<T> Prism практически для всего), поэтому, пожалуйста, не возлагайте на меня ответственность, если мой вызов CommandManager.InvalidateRequerySuggested запускает какой-то каскад коллапса квантовой волновой функции, который разрушает известную вселенную или что-то еще. .

using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Input;

namespace Microsoft.Practices.Composite.Wpf.Commands
{
    /// <summary>
    /// This class provides an attached property that, when set to true, will cause changes to the element's CommandParameter to 
    /// trigger the CanExecute handler to be called on the Command.
    /// </summary>
    public static class CommandParameterBehavior
    {
        /// <summary>
        /// Identifies the IsCommandRequeriedOnChange attached property
        /// </summary>
        /// <remarks>
        /// When a control has the <see cref="IsCommandRequeriedOnChangeProperty" />
        /// attached property set to true, then any change to it's 
        /// <see cref="System.Windows.Controls.Primitives.ButtonBase.CommandParameter" /> property will cause the state of
        /// the command attached to it's <see cref="System.Windows.Controls.Primitives.ButtonBase.Command" /> property to 
        /// be reevaluated.
        /// </remarks>
        public static readonly DependencyProperty IsCommandRequeriedOnChangeProperty =
            DependencyProperty.RegisterAttached("IsCommandRequeriedOnChange",
                                                typeof(bool),
                                                typeof(CommandParameterBehavior),
                                                new UIPropertyMetadata(false, new PropertyChangedCallback(OnIsCommandRequeriedOnChangeChanged)));

        /// <summary>
        /// Gets the value for the <see cref="IsCommandRequeriedOnChangeProperty"/> attached property.
        /// </summary>
        /// <param name="target">The object to adapt.</param>
        /// <returns>Whether the update on change behavior is enabled.</returns>
        public static bool GetIsCommandRequeriedOnChange(DependencyObject target)
        {
            return (bool)target.GetValue(IsCommandRequeriedOnChangeProperty);
        }

        /// <summary>
        /// Sets the <see cref="IsCommandRequeriedOnChangeProperty"/> attached property.
        /// </summary>
        /// <param name="target">The object to adapt. This is typically a <see cref="System.Windows.Controls.Primitives.ButtonBase" />, 
        /// <see cref="System.Windows.Controls.MenuItem" /> or <see cref="System.Windows.Documents.Hyperlink" /></param>
        /// <param name="value">Whether the update behaviour should be enabled.</param>
        public static void SetIsCommandRequeriedOnChange(DependencyObject target, bool value)
        {
            target.SetValue(IsCommandRequeriedOnChangeProperty, value);
        }

        private static void OnIsCommandRequeriedOnChangeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (!(d is ICommandSource))
                return;

            if (!(d is FrameworkElement || d is FrameworkContentElement))
                return;

            if ((bool)e.NewValue)
            {
                HookCommandParameterChanged(d);
            }
            else
            {
                UnhookCommandParameterChanged(d);
            }

            UpdateCommandState(d);
        }

        private static PropertyDescriptor GetCommandParameterPropertyDescriptor(object source)
        {
            return TypeDescriptor.GetProperties(source.GetType())["CommandParameter"];
        }

        private static void HookCommandParameterChanged(object source)
        {
            var propertyDescriptor = GetCommandParameterPropertyDescriptor(source);
            propertyDescriptor.AddValueChanged(source, OnCommandParameterChanged);

            // N.B. Using PropertyDescriptor.AddValueChanged will cause "source" to never be garbage collected,
            // so we need to hook the Unloaded event and call RemoveValueChanged there.
            HookUnloaded(source);
        }

        private static void UnhookCommandParameterChanged(object source)
        {
            var propertyDescriptor = GetCommandParameterPropertyDescriptor(source);
            propertyDescriptor.RemoveValueChanged(source, OnCommandParameterChanged);

            UnhookUnloaded(source);
        }

        private static void HookUnloaded(object source)
        {
            var fe = source as FrameworkElement;
            if (fe != null)
            {
                fe.Unloaded += OnUnloaded;
            }

            var fce = source as FrameworkContentElement;
            if (fce != null)
            {
                fce.Unloaded += OnUnloaded;
            }
        }

        private static void UnhookUnloaded(object source)
        {
            var fe = source as FrameworkElement;
            if (fe != null)
            {
                fe.Unloaded -= OnUnloaded;
            }

            var fce = source as FrameworkContentElement;
            if (fce != null)
            {
                fce.Unloaded -= OnUnloaded;
            }
        }

        static void OnUnloaded(object sender, RoutedEventArgs e)
        {
            UnhookCommandParameterChanged(sender);
        }

        static void OnCommandParameterChanged(object sender, EventArgs ea)
        {
            UpdateCommandState(sender);
        }

        private static void UpdateCommandState(object target)
        {
            var commandSource = target as ICommandSource;

            if (commandSource == null)
                return;

            var rc = commandSource.Command as RoutedCommand;
            if (rc != null)
            {
                CommandManager.InvalidateRequerySuggested();
            }

            var dc = commandSource.Command as IDelegateCommand;
            if (dc != null)
            {
                dc.RaiseCanExecuteChanged();
            }

        }
    }
}
person Swythan    schedule 18.02.2009
comment
наткнулся на ваш отчет об ошибке при подключении. Есть ли шанс, что вы могли бы обновить свой пост здесь своим последним кодом? или с тех пор вы нашли лучший вариант? - person Markus Hütter; 27.07.2011
comment
Более простым решением может быть наблюдение за свойством CommandParameter с использованием привязки вместо дескриптора свойства. В противном случае отличное решение! Это фактически устраняет основную проблему, а не просто вводит неудобный взлом или обходной путь. - person Sebastian Negraszus; 07.02.2019

Существует относительно простой способ «исправить» эту проблему с помощью DelegateCommand, хотя он требует обновления исходного кода DelegateCommand и повторной компиляции Microsoft.Practices.Composite.Presentation.dll.

1) Загрузите исходный код Prism 1.2 и откройте CompositeApplicationLibrary_Desktop.sln. Здесь находится проект Composite.Presentation.Desktop, который содержит исходный код DelegateCommand.

2) В общедоступном событии EventHandler CanExecuteChanged измените его следующим образом:

public event EventHandler CanExecuteChanged
{
     add
     {
          WeakEventHandlerManager.AddWeakReferenceHandler( ref _canExecuteChangedHandlers, value, 2 );
          // add this line
          CommandManager.RequerySuggested += value;
     }
     remove
     {
          WeakEventHandlerManager.RemoveWeakReferenceHandler( _canExecuteChangedHandlers, value );
          // add this line
          CommandManager.RequerySuggested -= value;
     }
}

3) В разделе protected virtual void OnCanExecuteChanged () измените его следующим образом:

protected virtual void OnCanExecuteChanged()
{
     // add this line
     CommandManager.InvalidateRequerySuggested();
     WeakEventHandlerManager.CallWeakReferenceHandlers( this, _canExecuteChangedHandlers );
}

4) Перекомпилируйте решение, затем перейдите в папку Debug или Release, в которой находятся скомпилированные библиотеки DLL. Скопируйте Microsoft.Practices.Composite.Presentation.dll и .pdb (если хотите) туда, где вы ссылаетесь на свои внешние сборки, а затем перекомпилируйте приложение, чтобы получить новые версии.

После этого CanExecute должен запускаться каждый раз, когда пользовательский интерфейс отображает элементы, привязанные к рассматриваемой DelegateCommand.

Береги себя, Джо

рефери в Gmail

person Joe Bako    schedule 08.04.2010

Прочитав несколько хороших ответов на похожие вопросы, я немного изменил в вашем примере DelegateCommand, чтобы заставить его работать. Вместо использования:

public event EventHandler CanExecuteChanged;

Я изменил его на:

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

Я удалил следующие два метода, потому что мне было лень их исправлять

public void RaiseCanExecuteChanged()

а также

protected virtual void OnCanExecuteChanged()

И это все ... похоже, это гарантирует, что CanExecute будет вызываться при изменении Binding и после метода Execute

Он не будет автоматически запускаться, если ViewModel будет изменен, но, как упоминалось в этом потоке, возможно, вызвав CommandManager.InvalidateRequerySuggested в потоке графического интерфейса

Application.Current?.Dispatcher.Invoke(DispatcherPriority.Normal, (Action)CommandManager.InvalidateRequerySuggested);
person kkCosmo    schedule 29.11.2015
comment
Я обнаружил, что DispatcherPriority.Normal слишком высок, чтобы работать надежно (или вообще, в моем случае). Использование DispatcherPriority.Loaded работает хорошо и кажется более подходящим (т.е. явно указывает, что делегат не должен вызываться до тех пор, пока элементы пользовательского интерфейса, связанные с моделью представления, не будут фактически загружены). - person Peter Duniho; 18.05.2016

Привет, Джонас, не уверен, будет ли это работать в шаблоне данных, но вот синтаксис привязки, который я использую в контекстном меню ListView, чтобы получить текущий элемент в качестве параметра команды:

CommandParameter = "{Binding RelativeSource = {RelativeSource AncestorType = ContextMenu}, Path = PlacementTarget.SelectedItem, Mode = TwoWay}"

person Community    schedule 04.12.2008
comment
Я делаю то же самое в моем представлении списка. В данном случае это ItemsControl, поэтому нет очевидного свойства для привязки (в визуальном дереве). Думаю, мне нужно найти способ определить, когда привязка выполнена, и повторно оценить CanExecute (потому что CommandParameter привязывается, просто слишком поздно) - person Jonas Follesø; 05.12.2008

Я зарегистрировал это как ошибку WPF в .Net 4.0, поскольку проблема все еще существует в бета-версии 2.

https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=504976

person Swythan    schedule 26.11.2009
comment
ссылка обратной связи больше не действительна - person Peter Duniho; 18.05.2016

Некоторые из этих ответов касаются привязки к DataContext для получения самой команды, но вопрос заключался в том, что CommandParameter имеет значение null, а не должно быть. Мы тоже это испытали. Догадавшись, мы нашли очень простой способ заставить это работать в нашей ViewModel. Это специально для нулевой проблемы CommandParameter, о которой сообщил заказчик, с одной строкой кода. Обратите внимание на Dispatcher.BeginInvoke ().

public DelegateCommand<objectToBePassed> CommandShowReport
    {
        get
        {
            // create the command, or pass what is already created.
            var command = _commandShowReport ?? (_commandShowReport = new DelegateCommand<object>(OnCommandShowReport, OnCanCommandShowReport));

            // For the item template, the OnCanCommand will first pass in null. This will tell the command to re-pass the command param to validate if it can execute.
            Dispatcher.BeginInvoke((Action) delegate { command.RaiseCanExecuteChanged(); }, DispatcherPriority.DataBind);

            return command;
        }
    }
person TravisWhidden    schedule 21.06.2016

Это долгий путь. чтобы отладить это, вы можете попробовать:
- проверить событие PreviewCanExecute.
- использовать snoop / wpf крот, чтобы заглянуть внутрь и посмотреть, что такое параметр команды.

HTH,

person Dennis    schedule 04.12.2008
comment
Пытался использовать Snoop - но отладить очень сложно, так как при первоначальной загрузке он только NULL. Если я запускаю Snoop на нем, Command и CommandParameter оба установлены ... Это связано с использованием команд в DataTemplate. - person Jonas Follesø; 04.12.2008

У меня тоже работает commandManager.InvalidateRequerySuggested. Я считаю, что следующая ссылка говорит об аналогичной проблеме, и M $ dev подтвердил ограничение в текущей версии, а commandManager.InvalidateRequerySuggested - это обходной путь. http://social.expression.microsoft.com/Forums/en-US/wpf/thread/c45d2272-e8ba-4219-bb41-1e5eaed08a1f/

Что важно, так это время вызова commandManager.InvalidateRequerySuggested. Это должно быть вызвано после уведомления о соответствующем изменении значения.

person Community    schedule 22.12.2008
comment
эта ссылка больше не действительна - person Peter Duniho; 18.05.2016

Помимо предложения Эда Болла об установке CommandParameter перед Command, сделайте убедитесь, что ваш метод CanExecute имеет параметр типа объект.

private bool OnDeleteSelectedItemsCanExecute(object SelectedItems)  
{
    // Your goes heres
}

Надеюсь, это помешает кому-то потратить огромное количество времени, которое я потратил, чтобы выяснить, как получить SelectedItems в качестве параметра CanExecute.

person Julio Nobre    schedule 22.09.2014