WPF CommandParameter là NULL lần đầu tiên CanExecute được gọi


86

Tôi đã gặp sự cố với WPF và Các lệnh được liên kết với một Nút bên trong DataTemplate của một ItemsControl. Kịch bản khá thẳng về phía trước. ItemsControl được liên kết với một danh sách các đối tượng và tôi muốn có thể xóa từng đối tượng trong danh sách bằng cách nhấp vào một Nút. Nút thực hiện một Lệnh và Lệnh sẽ xử lý việc xóa. CommandParameter được liên kết với Đối tượng mà tôi muốn xóa. Bằng cách đó, tôi biết người dùng đã nhấp vào gì. Người dùng chỉ có thể xóa các đối tượng "của riêng mình" - vì vậy tôi cần thực hiện một số kiểm tra trong lệnh gọi "CanExecute" của Lệnh để xác minh rằng người dùng có quyền phù hợp.

Vấn đề là tham số được truyền cho CanExecute là NULL vào lần đầu tiên nó được gọi - vì vậy tôi không thể chạy logic để bật / tắt lệnh. Tuy nhiên, nếu tôi luôn bật tính năng này và sau đó nhấp vào nút để thực hiện lệnh, thì CommandParameter sẽ được chuyển vào chính xác. Vì vậy, điều đó có nghĩa là ràng buộc chống lại CommandParameter đang hoạt động.

XAML cho ItemsControl và DataTemplate trông giống như sau:

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

Vì vậy, như bạn có thể thấy, tôi có một danh sách các đối tượng Nhận xét. Tôi muốn CommandParameter của DeleteCommentCommand được liên kết với đối tượng Command.

Vì vậy, tôi đoán câu hỏi của tôi là: đã có ai trải qua vấn đề này trước đây chưa? CanExecute được gọi trên Command của tôi, nhưng tham số luôn là NULL trong lần đầu tiên - tại sao vậy?

Cập nhật: Tôi đã có thể thu hẹp vấn đề xuống một chút. Tôi đã thêm một ValueConverter Debug trống để tôi có thể xuất ra thông báo khi CommandParameter bị ràng buộc dữ liệu. Hóa ra vấn đề là phương thức CanExecute được thực thi trước khi CommandParameter được liên kết với nút. Tôi đã cố gắng đặt CommandParameter trước Command (như được đề xuất) - nhưng nó vẫn không hoạt động. Bất kỳ lời khuyên về cách kiểm soát nó.

Update2: Có cách nào để phát hiện khi nào ràng buộc "xong", để tôi có thể buộc đánh giá lại lệnh không? Ngoài ra - có vấn đề là tôi có nhiều Nút (một cho mỗi mục trong ItemsControl) liên kết với cùng một phiên bản của đối tượng Lệnh không?

Cập nhật 3: Tôi đã tải lên bản sao của lỗi này lên SkyDrive của mình: http://cid-1a08c11c407c0d8e.skydrive.live.com/self.aspx/Code%20samples/CommandParameterBinding.zip


Tôi cũng gặp phải vấn đề tương tự với ListBox.
Hadi Eskandari

Hiện có một báo cáo lỗi mở đối với WPF cho vấn đề này: github.com/dotnet/wpf/issues/316
UuDdLrLrSs

Câu trả lời:


14

Tôi tình cờ gặp một vấn đề tương tự và đã giải quyết nó bằng TriggerConverter đáng tin cậy của mình.

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
}

Bộ chuyển đổi giá trị này nhận bất kỳ số lượng tham số nào và chuyển phần đầu tiên trở lại dưới dạng giá trị được chuyển đổi. Khi được sử dụng trong MultiBinding trong trường hợp của bạn, nó trông giống như sau.

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

Bạn sẽ phải thêm TriggerConverter làm tài nguyên ở đâu đó để nó hoạt động. Bây giờ thuộc tính Command không được đặt trước khi giá trị cho CommandParameter có sẵn. Bạn thậm chí có thể liên kết với RelativeSource.Self và CommandParameter thay vì. để đạt được hiệu quả tương tự.


2
Điều này đã làm việc cho tôi. Tôi không hiểu tại sao. Bất cứ ai có thể giải thích?
TJKjaer

Nó không hoạt động vì CommandParameter bị ràng buộc trước Command? Tôi nghi ngờ bạn sẽ cần trình chuyển đổi ...
MBoros

2
Đây không phải là một giải pháp. Đây là một vụ hack? Cái quái gì đang diễn ra vậy? Điều này đã từng làm việc?
Jordan

Hoàn hảo, phù hợp với tôi! Sự kỳ diệu là trong <Binding /> dòng, gây ra các ràng buộc để được cập nhật lệnh khi các mẫu dữ liệu thay đổi (được ràng buộc với tham số lệnh)
Andreas Kahler

56

Tôi đã gặp sự cố tương tự khi cố gắng liên kết với một lệnh trên mô hình chế độ xem của mình.

Tôi đã thay đổi nó để sử dụng liên kết nguồn tương đối thay vì tham chiếu đến phần tử theo tên và điều đó đã thực hiện một mẹo nhỏ. Tham số ràng buộc không thay đổi.

Mã cũ:

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

Mã mới:

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

Cập nhật : Tôi vừa gặp sự cố này mà không sử dụng ElementName, tôi đang ràng buộc với một lệnh trên mô hình chế độ xem của mình và ngữ cảnh dữ liệu của nút là mô hình chế độ xem của tôi. Trong trường hợp này, tôi chỉ cần di chuyển thuộc tính CommandParameter trước thuộc tính Command trong khai báo Button (trong XAML).

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

42
Di chuyển CommandParameter trước Command là câu trả lời tốt nhất trên chủ đề này.
BSick7

6
Việc di chuyển thứ tự của các thuộc tính không giúp được gì cho chúng tôi. Tôi sẽ ngạc nhiên nếu nó có ảnh hưởng đến trình tự thực hiện.
Jack Ukleja

3
Tôi không biết tại sao nó hoạt động. Nó cảm thấy như nó không nên nhưng nó hoàn toàn có.
RMK

1
Tôi gặp vấn đề tương tự - RelativeSource không giúp được gì, việc thay đổi thứ tự của các thuộc tính đã xảy ra. Cảm ơn các cập nhật!
Grant Crofton

14
Là một người tôn giáo sử dụng các tiện ích mở rộng để tự động làm đẹp XAML (phân chia các thuộc tính thành các dòng, sửa lỗi thụt lề, sắp xếp lại các thuộc tính), đề xuất thay đổi thứ tự CommandParameterCommandkhiến tôi sợ hãi.
Guttsy

29

Tôi nhận thấy rằng thứ tự mà tôi đặt Command và CommandParameter tạo ra sự khác biệt. Đặt thuộc tính Command khiến CanExecute được gọi ngay lập tức, vì vậy bạn muốn CommandParameter đã được đặt tại thời điểm đó.

Tôi nhận thấy rằng việc chuyển đổi thứ tự của các thuộc tính trong XAML thực sự có thể có tác dụng, mặc dù tôi không tự tin rằng nó sẽ giải quyết được vấn đề của bạn. Tuy nhiên, nó đáng để thử.

Có vẻ như bạn đang gợi ý rằng nút không bao giờ được bật, điều này thật đáng ngạc nhiên, vì tôi mong đợi CommandParameter sẽ được đặt ngay sau thuộc tính Command trong ví dụ của bạn. Việc gọi CommandManager.InvalidateRequerySuggested () có khiến nút được bật không?


3
Đã thử thiết lập CommandParameter trước Command - vẫn thực thi CanExecute, nhưng vẫn chuyển trong NULL ... Bummer - nhưng cảm ơn vì mẹo. Ngoài ra, gọi CommandManager.InvalidateRequerySuggested (); không tạo ra bất kỳ sự khác biệt nào.
Jonas Follesø

CommandManager.InvalidateRequerySuggested () đã giải quyết một vấn đề tương tự cho tôi. Cảm ơn!
MJS

13

Tôi đã nghĩ ra một tùy chọn khác để giải quyết vấn đề này mà tôi muốn chia sẻ. Bởi vì phương thức CanExecute của lệnh được thực thi trước khi thuộc tính CommandParameter được thiết lập, tôi đã tạo một lớp trợ giúp với thuộc tính đính kèm buộc phương thức CanExecute được gọi lại khi ràng buộc thay đổi.

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

Và sau đó trên nút bạn muốn liên kết tham số lệnh với ...

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

Tôi hy vọng điều này có thể giúp ai đó khác với vấn đề này.


Rất tốt, cảm ơn bạn. Tôi không thể tin rằng M $ đã không sửa điều này sau 8 năm. Rẽ!
McGarnagle

8

Đây là một chủ đề cũ, nhưng vì Google đã đưa tôi đến đây khi tôi gặp sự cố này, nên tôi sẽ thêm những gì phù hợp với tôi cho DataGridTemplateColumn bằng một nút.

Thay đổi ràng buộc từ:

CommandParameter="{Binding .}"

đến

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

Không chắc tại sao nó hoạt động, nhưng nó đã làm cho tôi.


Tôi đã thử cả hai câu trả lời điểm cao ở trên, nhưng câu trả lời này chỉ phù hợp với tôi. Có vẻ như bản thân vấn đề kiểm soát nội bộ không phải là ràng buộc, nhưng vẫn có nhiều người hiểu được nó đang làm việc với các câu trả lời trên. Cảm ơn!
Javidan

6

Gần đây tôi đã gặp phải vấn đề tương tự (đối với tôi đó là đối với các mục menu trong menu ngữ cảnh), tuy nhiên nó có thể không phải là giải pháp phù hợp cho mọi tình huống, tôi đã tìm thấy một cách khác (và ngắn hơn rất nhiều!) Để giải quyết vấn đề này vấn đề:

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

Bỏ qua Taggiải pháp dựa trên cơ sở cho trường hợp đặc biệt của menu ngữ cảnh, chìa khóa ở đây là liên kết CommandParameterthường xuyên, nhưng liên kết Commandvới bổ sung IsAsync=True. Điều này sẽ làm trì hoãn sự ràng buộc của lệnh thực (và do đó CanExecutelà lệnh gọi của nó ) một chút, vì vậy tham số sẽ có sẵn. Tuy nhiên, điều này có nghĩa là trong một khoảnh khắc ngắn, trạng thái đã bật có thể bị sai, nhưng đối với trường hợp của tôi, điều đó hoàn toàn có thể chấp nhận được.


5

Bạn có thể sử dụng của tôi CommandParameterBehaviormà tôi đã đăng lên diễn đàn Prism ngày hôm qua. Nó bổ sung hành vi bị thiếu trong đó một thay đổi đối với CommandParameternguyên nhân Commandđược truy vấn lại.

Có một số phức tạp ở đây gây ra bởi nỗ lực của tôi để tránh rò rỉ bộ nhớ gây ra nếu bạn gọi PropertyDescriptor.AddValueChangedmà không gọi sau PropertyDescriptor.RemoveValueChanged. Tôi thử và khắc phục điều đó bằng cách hủy đăng ký trình xử lý khi ekement được dỡ bỏ.

Có thể bạn sẽ cần xóa IDelegateCommandnội dung trừ khi bạn đang sử dụng Prism (và muốn thực hiện các thay đổi giống như tôi đối với thư viện Prism). Cũng lưu ý rằng chúng tôi thường không sử dụng RoutedCommands ở đây (chúng tôi sử dụng Prism DelegateCommand<T>cho hầu hết mọi thứ) vì vậy xin vui lòng không bắt tôi phải chịu trách nhiệm nếu lời kêu gọi của tôi CommandManager.InvalidateRequerySuggestedbắt đầu một số loại thác sụp đổ sóng lượng tử phá hủy vũ trụ đã biết hoặc bất cứ điều gì.

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

        }
    }
}

đã xem báo cáo lỗi của bạn tại kết nối. Bất kỳ cơ hội nào bạn có thể cập nhật bài đăng của mình ở đây với mã cuối cùng của bạn về điều này? hay bạn đã tìm thấy một công việc tốt hơn xung quanh?
Markus Hütter

Một giải pháp dễ dàng hơn có thể là quan sát thuộc tính CommandParameter bằng cách sử dụng một ràng buộc thay vì một bộ mô tả thuộc tính. Nếu không, một giải pháp tuyệt vời! Cách này thực sự khắc phục được sự cố cơ bản thay vì chỉ giới thiệu một cách giải quyết hoặc hack khó xử.
Sebastian Negraszus

1

Có một cách tương đối đơn giản để "khắc phục" sự cố này với DelegateCommand, mặc dù nó yêu cầu cập nhật nguồn DelegateCommand và biên dịch lại Microsoft.Practices.Composite.Presentation.dll.

1) Tải xuống mã nguồn Prism 1.2 và mở CompositeApplicationLibrary_Desktop.sln. Ở đây là một dự án Composite.Presentation.Desktop có chứa nguồn DelegateCommand.

2) Trong sự kiện công khai EventHandler CanExecuteChanged, sửa đổi để đọc như sau:

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) Trong void ảo được bảo vệ OnCanExecuteChanged (), hãy sửa đổi nó như sau:

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

4) Biên dịch lại giải pháp, sau đó điều hướng đến thư mục Gỡ lỗi hoặc Phát hành nơi chứa các tệp DLL đã biên dịch. Sao chép Microsoft.Practices.Composite.Presentation.dll và .pdb (nếu bạn muốn) vào nơi bạn tham chiếu các hội đồng bên ngoài của mình, sau đó biên dịch lại ứng dụng của bạn để kéo các phiên bản mới.

Sau đó, CanExecute sẽ được kích hoạt mỗi khi giao diện người dùng kết xuất các phần tử liên kết với DelegateCommand được đề cập.

Bảo trọng, Joe

trọng tài tại gmail


1

Sau khi đọc một số câu trả lời hay cho các câu hỏi tương tự, tôi đã thay đổi một chút trong ví dụ của bạn lệnh DelegateCommand để làm cho nó hoạt động. Thay vì sử dụng:

public event EventHandler CanExecuteChanged;

Tôi đã thay đổi nó thành:

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

Tôi đã xóa hai phương pháp sau vì quá lười biếng để sửa chúng

public void RaiseCanExecuteChanged()

protected virtual void OnCanExecuteChanged()

Và đó là tất cả ... điều này dường như đảm bảo rằng CanExecute sẽ được gọi khi Binding thay đổi và sau phương thức Execute

Nó sẽ không tự động kích hoạt nếu ViewModel bị thay đổi nhưng như đã đề cập trong luồng này, có thể bằng cách gọi CommandManager .InvalidateRequerySuggested trên luồng GUI

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

Tôi nhận thấy rằng đó DispatcherPriority.Normallà quá cao để làm việc đáng tin cậy (hoặc ở tất cả, trong trường hợp của tôi). Việc sử dụng DispatcherPriority.Loadedhoạt động tốt và có vẻ phù hợp hơn (nghĩa là chỉ ra rõ ràng rằng đại biểu sẽ không được gọi cho đến khi các phần tử giao diện người dùng liên kết với mô hình chế độ xem trên thực tế đã được tải).
Peter Duniho

0

Này Jonas, không chắc điều này có hoạt động trong mẫu dữ liệu hay không, nhưng đây là cú pháp liên kết mà tôi sử dụng trong menu Ngữ cảnh ListView để lấy mục hiện tại làm tham số lệnh:

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


Tôi làm điều tương tự trong chế độ xem danh sách của mình. Trong trường hợp này, nó là một ItemsControl nên không có thuộc tính rõ ràng nào để "ràng buộc" đối với (trong cây trực quan). Tôi đoán mình phải tìm cách phát hiện khi nào việc ràng buộc được thực hiện xong và đánh giá lại CanExecute (vì CommandParameter bị ràng buộc, chỉ đến muộn)
Jonas Follesø


0

Một số câu trả lời trong số này là về việc liên kết với DataContext để nhận được chính Command, nhưng câu hỏi là về việc CommandParameter là null khi không nên. Chúng tôi cũng đã trải qua điều này. Có một chút linh cảm, chúng tôi đã tìm thấy một cách rất đơn giản để làm cho điều này hoạt động trong ViewModel của chúng tôi. Điều này đặc biệt dành cho sự cố rỗng CommandParameter được khách hàng báo cáo, với một dòng mã. Lưu ý 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

Đó là một cú sút xa. để gỡ lỗi này, bạn có thể thử:
- kiểm tra sự kiện PreviewCanExecute.
- sử dụng snoop / wpf nốt ruồi để nhìn vào bên trong và xem tham số lệnh là gì.

HTH,


Đã thử sử dụng Snoop - nhưng thực sự khó gỡ lỗi vì nó chỉ NULL khi tải ban đầu. Nếu tôi chạy Snoop trên đó thì Command và CommandParameter đều giống nhau ... Nó liên quan đến việc sử dụng Lệnh trong DataTemplate.
Jonas Follesø

-1

CommandManager.InvalidateRequerySuggested cũng hoạt động với tôi. Tôi tin rằng liên kết sau nói về vấn đề tương tự và M $ dev đã xác nhận giới hạn trong phiên bản hiện tại và commandManager.InvalidateRequerySuggested là cách giải quyết. http://social.expression.microsoft.com/Forums/en-US/wpf/thread/c45d2272-e8ba-4219-bb41-1e5eaed08a1f/

Điều quan trọng là thời gian gọi commandManager.InvalidateRequerySuggested. Điều này sẽ được gọi sau khi thay đổi giá trị liên quan được thông báo.


liên kết đó không còn hợp lệ
Peter Duniho

-2

Bên cạnh gợi ý của Ed Ball về việc đặt CommandParameter trước Command , hãy đảm bảo rằng phương thức CanExecute của bạn có tham số kiểu đối tượng .

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

Hy vọng nó ngăn chặn ai đó dành một lượng lớn thời gian mà tôi đã làm để tìm ra cách nhận SelectedItems dưới dạng tham số CanExecute

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.