WPF CommandParameter ma wartość NULL po pierwszym wywołaniu CanExecute

86

Wystąpił problem z WPF i poleceniami, które są powiązane z przyciskiem wewnątrz DataTemplate elementu ItemsControl. Scenariusz jest dość prosty. ItemsControl jest powiązany z listą obiektów i chcę mieć możliwość usunięcia każdego obiektu z listy, klikając przycisk. Przycisk wykonuje polecenie, a polecenie dba o usunięcie. CommandParameter jest powiązany z obiektem, który chcę usunąć. W ten sposób wiem, co kliknął użytkownik. Użytkownik powinien mieć możliwość usuwania tylko swoich „własnych” obiektów - dlatego muszę sprawdzić wywołanie „CanExecute” polecenia, aby sprawdzić, czy użytkownik ma odpowiednie uprawnienia.

Problem polega na tym, że parametr przekazany do CanExecute ma wartość NULL przy pierwszym wywołaniu - więc nie mogę uruchomić logiki, aby włączyć / wyłączyć polecenie. Jeśli jednak włączę to zawsze, a następnie kliknę przycisk, aby wykonać polecenie, parametr CommandParameter zostanie przekazany poprawnie. Oznacza to, że działa powiązanie z CommandParameter.

XAML dla ItemsControl i DataTemplate wygląda następująco:

<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>

Jak widać, mam listę obiektów komentarzy. Chcę, aby CommandParameter z DeleteCommentCommand był powiązany z obiektem Command.

Więc wydaje mi się, że moje pytanie brzmi: czy ktoś wcześniej doświadczył tego problemu? CanExecute jest wywoływana przez moje polecenie, ale za pierwszym razem parametr ma zawsze wartość NULL - dlaczego tak jest?

Aktualizacja: udało mi się trochę zawęzić problem. Dodałem pusty Debug ValueConverter, aby móc wyprowadzić komunikat, gdy parametr CommandParameter jest powiązany z danymi. Okazuje się, że problem polega na tym, że metoda CanExecute jest wykonywana przed przypisaniem CommandParameter do przycisku. Próbowałem ustawić CommandParameter przed poleceniem (jak sugerowano) - ale nadal nie działa. Wszelkie wskazówki, jak to kontrolować.

Aktualizacja2: Czy istnieje sposób na wykrycie, kiedy powiązanie jest „gotowe”, aby wymusić ponowną ocenę polecenia? Ponadto - czy jest to problem, że mam wiele przycisków (po jednym dla każdego elementu w ItemsControl), które są powiązane z tym samym wystąpieniem obiektu polecenia?

Update3: Przesłałem reprodukcję błędu do mojego SkyDrive: http://cid-1a08c11c407c0d8e.skydrive.live.com/self.aspx/Code%20samples/CommandParameterBinding.zip

Jonas Follesø
źródło
Mam dokładnie ten sam problem z ListBox.
Hadi Eskandari
Obecnie istnieje otwarty raport błędu dotyczący WPF dotyczący tego problemu: github.com/dotnet/wpf/issues/316
UuDdLrLrSs

Odpowiedzi:

14

Natknąłem się na podobny problem i rozwiązałem go za pomocą mojego zaufanego 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
}

Ten konwerter wartości przyjmuje dowolną liczbę parametrów i przekazuje pierwszy z nich z powrotem jako przekonwertowaną wartość. Kiedy jest używany w MultiBinding w twoim przypadku, wygląda to następująco.

<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>

Aby to zadziałało, musisz gdzieś dodać TriggerConverter jako zasób. Teraz właściwość Command jest ustawiana nie przed udostępnieniem wartości parametru CommandParameter. Możesz nawet powiązać z RelativeSource.Self i CommandParameter zamiast. aby osiągnąć ten sam efekt.

David Liersch
źródło
2
To zadziałało dla mnie. Nie rozumiem dlaczego. Czy ktoś może wyjaśnić?
TJKjaer
Czy to nie działa, ponieważ CommandParameter jest powiązany przed poleceniem? Wątpię, żebyś potrzebował konwertera ...
MBoros,
2
To nie jest rozwiązanie. To jest hack? Co się do cholery dzieje? To działało?
Jordan
Idealnie działa na mnie! Magia tkwi w wierszu <Binding />, co powoduje, że powiązanie polecenia jest aktualizowane, gdy zmienia się szablon danych (który jest powiązany z parametrem polecenia)
Andreas Kahler
56

Miałem ten sam problem podczas próby powiązania z poleceniem w moim modelu widoku.

Zmieniłem to, aby używać względnego powiązania źródła zamiast odwoływania się do elementu według nazwy i to załatwiło sprawę. Powiązanie parametrów nie uległo zmianie.

Stary kod:

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

Nowy kod:

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

Aktualizacja : właśnie natknąłem się na ten problem bez używania ElementName, wiążę się z poleceniem w moim modelu widoku, a mój kontekst danych przycisku to mój model widoku. W tym przypadku musiałem po prostu przenieść atrybut CommandParameter przed atrybutem Command w deklaracji przycisku (w XAML).

CommandParameter="{Binding Groups}"
Command="{Binding StartCommand}"
Travis Weber
źródło
42
Przeniesienie CommandParameter przed Command jest najlepszą odpowiedzią w tym wątku.
BSick7,
6
Zmiana kolejności atrybutów nie pomogła nam. Zdziwiłbym się, gdyby miało to wpływ na kolejność egzekucji.
Jack Ukleja,
3
Nie wiem, dlaczego to działa. Wydaje się, że nie powinno, ale całkowicie tak.
RMK,
1
Miałem ten sam problem - RelativeSource nie pomogło, zmieniła kolejność atrybutów. Dziękuję za aktualizację!
Grant Crofton
14
Jako osoba, która religijnie używa rozszerzeń do automatycznego upiększania XAML (dzielenie atrybutów na linie, naprawianie wcięć, zmiana kolejności atrybutów) propozycja zmiany kolejności CommandParameteri Commandprzeraża mnie.
Guttsy,
29

Odkryłem, że kolejność, w której ustawiam Command i CommandParameter, robi różnicę. Ustawienie właściwości Command powoduje natychmiastowe wywołanie CanExecute, więc chcesz, aby CommandParameter był już ustawiony w tym momencie.

Odkryłem, że zmiana kolejności właściwości w XAML może faktycznie mieć wpływ, chociaż nie jestem pewien, czy rozwiąże Twój problem. Warto jednak spróbować.

Wydaje się, że sugerujesz, że przycisk nigdy nie zostanie włączony, co jest zaskakujące, ponieważ spodziewałbym się, że CommandParameter zostanie ustawiony wkrótce po właściwości Command w twoim przykładzie. Czy wywołanie CommandManager.InvalidateRequerySuggested () powoduje włączenie przycisku?

Ed Ball
źródło
3
Próbowałem ustawić parametr CommandParameter przed poleceniem - nadal wykonuje CanExecute, ale nadal przekazuje NULL ... Bummer - ale dzięki za wskazówkę. Również wywołanie CommandManager.InvalidateRequerySuggested (); nie ma znaczenia.
Jonas Follesø
CommandManager.InvalidateRequerySuggested () rozwiązało podobny problem. Dzięki!
MJS
13

Wymyśliłem inną opcję obejścia tego problemu, którą chciałem się podzielić. Ponieważ metoda CanExecute polecenia jest wykonywana przed ustawieniem właściwości CommandParameter, utworzyłem klasę pomocniczą z dołączoną właściwością, która wymusza ponowne wywołanie metody CanExecute po zmianie powiązania.

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);
    }
}

A następnie na przycisku, do którego chcesz przypisać parametr polecenia ...

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

Mam nadzieję, że to może pomóc komuś innemu w rozwiązaniu problemu.

Ed Downs
źródło
Dobra robota, dziękuję. Nie mogę uwierzyć, że M $ nie naprawił tego po 8 latach. Turrible!
McGarnagle
8

To jest stary wątek, ale ponieważ Google przywiózł mnie tutaj, kiedy miałem ten problem, dodam to, co działało dla mnie dla DataGridTemplateColumn z przyciskiem.

Zmień oprawę z:

CommandParameter="{Binding .}"

do

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

Nie wiem, dlaczego to działa, ale mi się udało.

Simon Smith
źródło
Wypróbowałem obie odpowiedzi z najlepszymi wynikami powyżej, ale ta zadziałała tylko dla mnie. Wygląda na to, że jest to wewnętrzny problem dotyczący kontroli, a nie obowiązujący, ale nadal wiele osób zrozumiało, że działa z powyższymi odpowiedziami. Dzięki!
Javidan,
6

Niedawno natknąłem się na ten sam problem (dla mnie dotyczyło to pozycji menu w menu kontekstowym) i chociaż może to nie być odpowiednie rozwiązanie w każdej sytuacji, znalazłem inny (i znacznie krótszy!) Sposób rozwiązania tego problemu problem:

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

Ignorując Tagobejście oparte na -bazie dla specjalnego przypadku menu kontekstowego, kluczem jest tutaj CommandParameterregularne wiązanie, ale wiązanie Commandz dodatkowym IsAsync=True. Spowoduje CanExecuteto nieco opóźnienie wiązania rzeczywistego polecenia (a tym samym jego wywołania), więc parametr będzie już dostępny. Oznacza to jednak, że przez krótką chwilę stan włączenia może być zły, ale w moim przypadku było to całkowicie do przyjęcia.

Ralf Stauder
źródło
5

Być może będziesz mógł użyć mojego, CommandParameterBehaviorktóre opublikowałem wczoraj na forach Prism . Dodaje brakujące zachowanie, w którym zmiana CommandParameterprzyczyny Commandma być odpytywana.

Jest tu pewna złożoność spowodowana przez moje próby uniknięcia wycieku pamięci spowodowanego dzwonieniem PropertyDescriptor.AddValueChangedbez późniejszego dzwonieniaPropertyDescriptor.RemoveValueChanged . Próbuję to naprawić, wyrejestrowując handler, gdy ekement jest rozładowany.

Prawdopodobnie będziesz musiał usunąć IDelegateCommandrzeczy, chyba że używasz Prism (i chcesz wprowadzić takie same zmiany jak ja w bibliotece Prism). Zauważ również, że generalnie nie używamy RoutedCommandtutaj s (używamy Prism DelegateCommand<T>do prawie wszystkiego), więc proszę nie CommandManager.InvalidateRequerySuggestedpociągaj mnie do odpowiedzialności, jeśli moje wezwanie do wywołania jakiejś kaskady zapaści fal kwantowych, która zniszczy znany wszechświat lub cokolwiek innego.

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();
            }

        }
    }
}
Swythan
źródło
natrafiłem na twój raport o błędzie w connect. Jest jakaś szansa, że ​​mógłbyś zaktualizować swój post tutaj ostatnim kodem? czy od tego czasu znalazłeś lepsze rozwiązanie?
Markus Hütter,
Łatwiejszym rozwiązaniem może być obserwowanie właściwości CommandParameter przy użyciu powiązania zamiast deskryptora właściwości. W przeciwnym razie świetne rozwiązanie! Ten faktycznie rozwiązuje podstawowy problem, zamiast po prostu wprowadzić niezręczny hack lub obejście.
Sebastian Negraszus
1

Istnieje stosunkowo prosty sposób „naprawienia” tego problemu za pomocą narzędzia DelegateCommand, chociaż wymaga on zaktualizowania źródła DelegateCommand i ponownej kompilacji Microsoft.Practices.Composite.Presentation.dll.

1) Pobierz kod źródłowy Prism 1.2 i otwórz plik CompositeApplicationLibrary_Desktop.sln. Tutaj znajduje się projekt Composite.Presentation.Desktop, który zawiera źródło DelegateCommand.

2) W ramach zdarzenia publicznego EventHandler CanExecuteChanged zmodyfikuj, aby brzmiało następująco:

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) W obszarze chronionego wirtualnego void OnCanExecuteChanged () zmodyfikuj go w następujący sposób:

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

4) Ponownie skompiluj rozwiązanie, a następnie przejdź do folderu Debug lub Release, w którym znajdują się skompilowane biblioteki DLL. Skopiuj Microsoft.Practices.Composite.Presentation.dll i .pdb (jeśli chcesz) do miejsca, w którym odwołujesz się do zestawów zewnętrznych, a następnie ponownie skompiluj aplikację, aby pobrać nowe wersje.

Następnie CanExecute powinno być uruchamiane za każdym razem, gdy interfejs użytkownika renderuje elementy powiązane z danym DelegateCommand.

Uważaj, Joe

refereejoe na gmail

Joe Bako
źródło
1

Po przeczytaniu kilku dobrych odpowiedzi na podobne pytania, w twoim przykładzie nieznacznie zmieniłem DelegateCommand, aby działało. Zamiast używać:

public event EventHandler CanExecuteChanged;

Zmieniłem to na:

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

Usunąłem następujące dwie metody, ponieważ byłem zbyt leniwy, aby je naprawić

public void RaiseCanExecuteChanged()

i

protected virtual void OnCanExecuteChanged()

I to wszystko ... wydaje się, że zapewnia to wywołanie CanExecute, gdy Binding się zmieni i po metodzie Execute

Nie zostanie automatycznie wyzwolony, jeśli ViewModel zostanie zmieniony, ale jak wspomniano w tym wątku, jest to możliwe przez wywołanie polecenia CommandManager.InvalidateRequerySuggested w wątku GUI

Application.Current?.Dispatcher.Invoke(DispatcherPriority.Normal, (Action)CommandManager.InvalidateRequerySuggested);
kkCosmo
źródło
Stwierdziłem, że DispatcherPriority.Normaljest to zbyt wysokie, aby działać niezawodnie (lub w ogóle, w moim przypadku). Używanie DispatcherPriority.Loadeddziała dobrze i wydaje się bardziej odpowiednie (tj. Wyraźnie wskazuje, że delegat nie ma być wywoływany, dopóki elementy interfejsu użytkownika skojarzone z modelem widoku nie zostaną w rzeczywistości załadowane).
Peter Duniho
0

Hej Jonas, nie jestem pewien, czy to zadziała w szablonie danych, ale oto składnia powiązania, której używam w menu kontekstowym ListView, aby pobrać bieżący element jako parametr polecenia:

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


źródło
Robię dokładnie to samo w widoku listy. W tym przypadku jest to ItemsControl, więc nie ma oczywistej właściwości, z którą można „powiązać” (w drzewie wizualnym). Myślę, że muszę znaleźć sposób na wykrycie, kiedy wiązanie jest zakończone, i ponownie ocenić CanExecute (ponieważ CommandParameter zostaje powiązany, tylko za późno)
Jonas Follesø,
0

Niektóre z tych odpowiedzi dotyczą powiązania z DataContext w celu pobrania samego polecenia, ale pytanie dotyczyło wartości parametru CommandParameter null, gdy nie powinno. My też tego doświadczyliśmy. W pewnym sensie znaleźliśmy bardzo prosty sposób, aby to zadziałało w naszym ViewModel. Dotyczy to w szczególności problemu zerowego parametru CommandParameter zgłoszonego przez klienta z jednym wierszem kodu. Zwróć uwagę na 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;
        }
    }
TravisWhidden
źródło
-1

To długa szansa. aby to debugować, możesz spróbować:
- sprawdzić zdarzenie PreviewCanExecute.
- użyj snoop / wpf mole, aby zajrzeć do środka i zobaczyć, jaki jest parametr polecenia.

HTH,

Dennis
źródło
Próbowałem przy użyciu Snoopa - ale jest to naprawdę trudne do debugowania, ponieważ ma wartość NULL tylko wtedy, gdy jest początkowo załadowana. Jeśli uruchomię na nim Snoopa, zarówno Command, jak i CommandParameter są ustawione na ... Ma to związek z używaniem poleceń w DataTemplate.
Jonas Follesø
-1

U mnie działa również commandManager.InvalidateRequerySuggested. Myślę, że poniższy link mówi o podobnym problemie, a M $ dev potwierdził ograniczenie w aktualnej wersji, a commandManager.InvalidateRequerySuggested stanowi obejście tego problemu. http://social.expression.microsoft.com/Forums/en-US/wpf/thread/c45d2272-e8ba-4219-bb41-1e5eaed08a1f/

Ważny jest czas wywołania polecenia commandManager.InvalidateRequerySuggested. Powinno to zostać wywołane po powiadomieniu o odpowiedniej zmianie wartości.


źródło
ten link nie jest już ważny
Peter Duniho
-2

Oprócz sugestii Eda Ball'a dotyczącej ustawienia CommandParameter przed Command , upewnij się, że metoda CanExecute ma parametr typu obiektu .

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

Mam nadzieję, że uniemożliwi to komuś spędzenie ogromnej ilości czasu, aby dowiedzieć się, jak odebrać SelectedItems jako parametr CanExecute

Julio Nobre
źródło