CanExecute가 처음 호출 될 때 WPF CommandParameter가 NULL입니다.


86

ItemsControl의 DataTemplate 내에있는 Button에 바인딩 된 WPF 및 명령에 문제가 발생했습니다. 시나리오는 매우 간단합니다. ItemsControl은 개체 목록에 바인딩되어 있으며 단추를 클릭하여 목록에서 각 개체를 제거 할 수 있기를 원합니다. 버튼은 명령을 실행하고 명령은 삭제를 처리합니다. CommandParameter는 삭제하려는 개체에 바인딩되어 있습니다. 그렇게하면 사용자가 무엇을 클릭했는지 알 수 있습니다. 사용자는 "자신의"개체 만 삭제할 수 있어야합니다. 따라서 사용자에게 올바른 권한이 있는지 확인하기 위해 Command의 "CanExecute"호출에서 몇 가지 검사를 수행해야합니다.

문제는 CanExecute에 전달 된 매개 변수가 처음 호출 될 때 NULL이라는 것입니다. 따라서 명령을 활성화 / 비활성화하는 논리를 실행할 수 없습니다. 그러나 항상 활성화 한 다음 버튼을 클릭하여 명령을 실행하면 CommandParameter가 올바르게 전달됩니다. 즉, CommandParameter에 대한 바인딩이 작동 중입니다.

ItemsControl 및 DataTemplate에 대한 XAML은 다음과 같습니다.

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

보시다시피 Comments 개체 목록이 있습니다. DeleteCommentCommand의 CommandParameter를 Command 개체에 바인딩하고 싶습니다.

그래서 내 질문은 : 누군가이 전에이 문제를 경험 한 적이 있습니까? CanExecute는 내 명령에서 호출되지만 매개 변수는 처음에 항상 NULL입니다. 그 이유는 무엇입니까?

업데이트 : 문제를 조금 좁힐 수있었습니다. CommandParameter가 데이터 바인딩 될 때 메시지를 출력 할 수 있도록 빈 Debug ValueConverter를 추가했습니다. 문제는 CommandParameter가 단추에 바인딩되기 전에 CanExecute 메서드가 실행된다는 것입니다. 명령 (제안 된 것처럼) 전에 CommandParameter를 설정하려고 시도했지만 여전히 작동하지 않습니다. 그것을 제어하는 ​​방법에 대한 모든 팁.

Update2 : 명령을 강제로 재평가 할 수 있도록 바인딩이 "완료"된시기를 감지하는 방법이 있습니까? 또한-Command-object의 동일한 인스턴스에 바인딩되는 여러 Button (ItemsControl의 각 항목에 대해 하나씩)이 있다는 것이 문제입니까?

Update3 : 내 SkyDrive에 버그 재현을 업로드했습니다 : http://cid-1a08c11c407c0d8e.skydrive.live.com/self.aspx/Code%20samples/CommandParameterBinding.zip


ListBox와 똑같은 문제가 있습니다.
Hadi Eskandari

이 문제에 대한 WPF에 대한 현재 열려있는 버그 보고서가 있습니다. github.com/dotnet/wpf/issues/316
UuDdLrLrSs

답변:


14

비슷한 문제를 발견하고 신뢰할 수있는 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를 어딘가에 리소스로 추가해야합니다. 이제 CommandParameter의 값을 사용할 수있게되기 전에 Command 속성이 설정됩니다. 대신 RelativeSource.Self 및 CommandParameter에 바인딩 할 수도 있습니다. 동일한 효과를 얻을 수 있습니다.


2
이것은 나를 위해 일했습니다. 이유를 이해하지 못합니다. 누구든지 설명 할 수 있습니까?
TJKjaer 2013 년

작동하지 않으면 CommandParameter가 Command 전에 바인딩됩니까? 나는 ... 당신이 컨버터를 필요 의심
MBoros

2
이것은 해결책이 아닙니다. 이것은 해킹입니까? 도대체 무슨 일이야? 이게 작동 했나요?
Jordan

완벽합니다. 마법은 <Binding /> 줄에 있습니다. 데이터 템플릿이 변경되면 명령 바인딩이 업데이트됩니다 (명령 매개 변수에 바인딩 됨)
Andreas Kahler

57

내 뷰 모델의 명령에 바인딩하는 동안 이와 동일한 문제가 발생했습니다.

이름으로 요소를 참조하는 대신 상대 소스 바인딩을 사용하도록 변경했으며 그게 트릭이었습니다. 매개 변수 바인딩이 변경되지 않았습니다.

이전 코드 :

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

새 코드 :

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

업데이트 : ElementName을 사용하지 않고 방금이 문제를 발견했으며 내 뷰 모델의 명령에 바인딩하고 있으며 버튼의 데이터 컨텍스트는 내 뷰 모델입니다. 이 경우에는 Button 선언 (XAML에서)의 Command 특성 앞에 CommandParameter 특성을 이동해야했습니다.

CommandParameter="{Binding Groups}"
Command="{Binding StartCommand}"

42
이 스레드에서 CommandParameter를 Command 앞으로 이동하는 것이 가장 좋은 대답입니다.
BSick7

6
속성 순서를 이동해도 도움이되지 않았습니다. 처형 순서에 영향을 주면 깜짝 놀랄 것입니다.
Jack Ukleja 2011 년

3
왜 이것이 작동하는지 모르겠습니다. 하지 말아야 할 것 같지만 완전히 그렇습니다.
RMK

1
나는 똑같은 문제가 있었다-RelativeSource는 도움이되지 않았고 속성의 순서를 변경했습니다. 업데이트 해주셔서 감사합니다!
Grant Crofton

14
종교적으로 XAML 아름답게 자동 확장을 사용하는 사람 (분할 선을 가로 질러 속성 수정 들여 쓰기, 재정렬 특성)의 순서 변경의 제안으로 CommandParameter하고 Command겁 나.
Guttsy 2014

29

Command 및 CommandParameter를 설정하는 순서가 차이가 있음을 발견했습니다. Command 속성을 설정하면 CanExecute가 즉시 호출되므로 CommandParameter가 해당 지점에 이미 설정되어 있어야합니다.

XAML에서 속성 순서를 전환하면 실제로 효과가있을 수 있지만 문제가 해결 될 것이라고 확신하지는 못합니다. 그래도 시도해 볼 가치가 있습니다.

버튼이 활성화되지 않는다고 제안하는 것 같습니다. 놀랍습니다. 예제의 Command 속성 바로 뒤에 CommandParameter가 설정 될 것으로 예상하기 때문입니다. CommandManager.InvalidateRequerySuggested ()를 호출하면 버튼이 활성화됩니까?


3
명령 전에 CommandParameter를 설정하려고 시도했습니다. 여전히 CanExecute를 실행하지만 여전히 NULL을 전달합니다 ... Bummer-하지만 팁을 주셔서 감사합니다. 또한 CommandManager.InvalidateRequerySuggested (); 차이가 없습니다.
Jonas Follesø

CommandManager.InvalidateRequerySuggested ()는 비슷한 문제를 해결했습니다. 감사!
MJS

13

공유하고 싶은이 문제를 해결하기위한 또 다른 옵션이 있습니다. CommandParameter 속성이 설정되기 전에 명령의 CanExecute 메서드가 실행되기 때문에 바인딩이 변경 될 때 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}" />

이 문제가 다른 사람에게 도움이되기를 바랍니다.


잘하셨습니다. 감사합니다. M $가 8 년이 지난 후에도이 문제를 해결하지 못했다는 것이 믿기지 않습니다. 끔찍하다!
McGarnagle 2016

8

이것은 오래된 스레드이지만이 문제가 발생했을 때 Google이 나를 여기로 데려 왔기 때문에 버튼이있는 DataGridTemplateColumn에 대해 저에게 도움이 된 것을 추가 할 것입니다.

다음에서 바인딩 변경 :

CommandParameter="{Binding .}"

...에

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

왜 작동하는지 확실하지 않지만 나에게는 효과적이었습니다.


나는 위의 두 가지 높은 점수 답변을 모두 시도했지만 이것은 나에게만 효과적이었습니다. 구속력이 아닌 통제 자체의 내부 문제인 것 같지만 여전히 많은 사람들이 위의 답변으로 작업하고 있습니다. 감사!
Javidan

6

나는 최근에 동일한 문제를 발견했습니다 (나에게는 상황에 맞는 메뉴의 메뉴 항목에 대한 것이 었습니다) nad 모든 상황에 적합한 솔루션은 아니지만이를 해결하는 다른 (그리고 훨씬 더 짧습니다!) 방법을 찾았습니다. 문제:

<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호출) 의 바인딩이 약간 지연 되므로 매개 변수를 이미 사용할 수 있습니다. 즉, 잠시 동안 활성화 상태가 잘못되었을 수 있지만 제 경우에는 완벽하게 수용 할 수있었습니다.


5

어제 Prism 포럼에CommandParameterBehavior 게시 한 나의 것을 사용하실 수 있습니다 . 원인이 변경되면 누락 된 동작을 추가합니다 .CommandParameterCommand 다시 조회 할 수 있습니다.

PropertyDescriptor.AddValueChanged나중에 호출하지 않고 호출 하면 발생하는 메모리 누수를 피하려는 시도로 인해 여기에 약간의 복잡성이 있습니다.PropertyDescriptor.RemoveValueChanged . 나는 ekement가 언로드 될 때 핸들러를 등록 해제함으로써 그것을 고치려고한다.

IDelegateCommandPrism을 사용하지 않는 한 (그리고 Prism 라이브러리에 저와 같은 변경을하고 싶지 않다면) 아마도 항목을 제거해야 할 것입니다 . 또한 우리는 일반적으로 RoutedCommand여기에서 s를 사용하지 않습니다 (우리는 DelegateCommand<T>거의 모든 것에 프리즘을 사용합니다 ). 그래서 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();
            }

        }
    }
}

connect에서 버그 보고서를 발견했습니다. 마지막 코드로 여기에서 게시물을 업데이트 할 수 있습니까? 아니면 그 이후로 더 나은 해결책을 찾았습니까?
Markus Hütter 2011

더 쉬운 해결책은 속성 설명자 대신 바인딩을 사용하여 CommandParameter 속성을 관찰하는 것입니다. 그렇지 않으면 훌륭한 솔루션입니다! 이것은 어색한 해킹이나 해결 방법을 도입하는 대신 실제로 근본적인 문제를 해결합니다.
Sebastian Negraszus

1

DelegateCommand 소스를 업데이트하고 Microsoft.Practices.Composite.Presentation.dll을 다시 컴파일해야하지만 DelegateCommand로이 문제를 "수정"하는 비교적 간단한 방법이 있습니다.

1) Prism 1.2 소스 코드를 다운로드하고 CompositeApplicationLibrary_Desktop.sln을 엽니 다. 여기에는 DelegateCommand 소스가 포함 된 Composite.Presentation.Desktop 프로젝트가 있습니다.

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) 보호 된 가상 void OnCanExecuteChanged ()에서 다음과 같이 수정합니다.

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

4) 솔루션을 다시 컴파일 한 다음 컴파일 된 DLL이있는 Debug 또는 Release 폴더로 이동합니다. Microsoft.Practices.Composite.Presentation.dll 및 .pdb (원하는 경우)를 외부 어셈블리를 참조하는 위치에 복사 한 다음 응용 프로그램을 다시 컴파일하여 새 버전을 가져옵니다.

그런 다음 UI가 해당 DelegateCommand에 바인딩 된 요소를 렌더링 할 때마다 CanExecute가 실행되어야합니다.

조심해, 조

Gmail의 refereejoe


1

유사한 질문에 대한 좋은 답변을 읽은 후 예제에서 DelegateCommand를 약간 변경하여 작동합니다. 사용하는 대신:

public event EventHandler CanExecuteChanged;

나는 그것을 다음과 같이 변경했다.

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

나는 그들을 고치기에는 너무 게으 르기 때문에 다음 두 가지 방법을 제거했습니다.

public void RaiseCanExecuteChanged()

protected virtual void OnCanExecuteChanged()

그리고 그게 다야 ... 이것은 Binding이 변경 될 때와 Execute 메서드 후에 CanExecute가 호출되도록 보장하는 것 같습니다.

ViewModel이 변경되면 자동으로 트리거되지 않지만이 스레드에서 언급 한대로 GUI 스레드에서 CommandManager.InvalidateRequerySuggested를 호출하여 가능합니다.

Application.Current?.Dispatcher.Invoke(DispatcherPriority.Normal, (Action)CommandManager.InvalidateRequerySuggested);

나는 그것이 DispatcherPriority.Normal안정적으로 작동하기에는 너무 높다는 것을 발견했습니다 (또는 제 경우에는 전혀). 사용 DispatcherPriority.Loaded이 잘 작동하고 더 적절 해 보입니다 (즉, 뷰 모델과 관련된 UI 요소가 실제로로드 될 때까지 델리게이트가 호출되지 않음을 명시 적으로 나타냄).
Peter Duniho

0

Jonas, 이것이 데이터 템플릿에서 작동하는지 확실하지 않지만 다음은 ListView 컨텍스트 메뉴에서 명령 매개 변수로 현재 항목을 가져 오는 데 사용하는 바인딩 구문입니다.

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


내 목록보기에서 똑같은 일을합니다. 이 경우 ItemsControl이므로 시각적 트리에서 "바인딩"할 명백한 속성이 없습니다. 바인딩이 끝났을 때 감지하는 방법을 찾고 CanExecute를 재평가해야한다고 생각합니다 (CommandParameter가 바인딩되기 때문에 늦게까지)
Jonas Follesø


0

이 답변 중 일부는 Command 자체를 가져 오기 위해 DataContext에 바인딩하는 것에 관한 것이지만 문제는 CommandParameter가 null이 아니어야 할 때 null이라는 것입니다. 우리도 이것을 경험했습니다. 직감으로 ViewModel에서이 작업을 수행하는 매우 간단한 방법을 찾았습니다. 이것은 특히 고객이 한 줄의 코드로보고 한 CommandParameter null 문제에 대한 것입니다. 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;
        }
    }

-1

긴 샷입니다. 이것을 디버깅하려면 다음을 시도 할 수 있습니다.
-PreviewCanExecute 이벤트를 확인합니다.
-snoop / wpf mole을 사용하여 내부를 들여다보고 commandparameter가 무엇인지 확인합니다.

HTH,


Snoop을 사용하여 시도했지만 처음로드 될 때 NULL 일 뿐이므로 디버그하기가 정말 어렵습니다. Snoop을 실행하면 Command와 CommandParameter가 모두 설정됩니다. DataTemplate에서 명령을 사용하는 것과 관련이 있습니다.
Jonas Follesø

-1

commandManager.InvalidateRequerySuggested도 저에게 효과적입니다. 다음 링크에서 비슷한 문제에 대해 이야기하고 M $ dev가 현재 버전의 제한을 확인했으며 commandManager.InvalidateRequerySuggested가 해결 방법이라고 생각합니다. http://social.expression.microsoft.com/Forums/en-US/wpf/thread/c45d2272-e8ba-4219-bb41-1e5eaed08a1f/

중요한 것은 commandManager.InvalidateRequerySuggested를 호출하는 타이밍입니다. 관련 값 변경이 통보 된 후에 호출되어야합니다.


그 링크는 더 이상 유효하지 않습니다
Peter Duniho

-2

Command 이전에 CommandParameter 설정에 대한 Ed Ball의 제안 외에도 CanExecute 메서드에 개체 유형 의 매개 변수가 있는지 확인하십시오 .

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

누군가가 내가 CanExecute 매개 변수로 SelectedItems를받는 방법을 알아 내기 위해했던 엄청난 시간을 소비하지 않기를 바랍니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.