Command<int> CanExecute 在 WPF 视图中无法正常工作

问题描述 投票:0回答:1

我目前正在处理 WPF 屏幕的视图,在该视图中,我们遇到了

Command<int>
Command<double>
类型的命令的异常问题。

在我们的应用程序中,有一个绑定了

Command<int>
的控件。我们还将
CanExecute
方法绑定到命令,该方法应根据当前条件启用或禁用控件。但是,当我们将命令定义为
RelayCommandWpf<int>
时,
CanExecute
函数将停止正常工作 - 它仅在初始化时触发一次,此后不再触发。

令人惊讶的是,如果我们将命令类型更改为

RelayCommandWpf<string>
CanExecute
方法将按预期工作,并且控件将按其应有的方式启用/禁用。

我的代码基本上是这样定义的:

命令

public RelayCommandWpf<int> MotorCommand => _motorCommand ??= new RelayCommandWpf<int>(SendMotorConfig, CanSendMotorConfig);

XAML

<Window x:Class="MotorViewExample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Motor View Example" Height="200" Width="300">
    <StackPanel>
        <Button Content="Execute Motor Command" 
                Command="{Binding MotorCommand}" 
                CommandParameter="5" 
                Width="200" 
                Height="50"/>
    </StackPanel>
</Window>

RelayCommandWpf:

using System;
using System.Diagnostics.CodeAnalysis;
using System.Windows.Input;

namespace VGUI.Boilerplate
{
    /// <summary>
    /// A generic command whose sole purpose is to relay its functionality to other objects by
    /// invoking delegates. The default return value for the CanExecute method is 'true'. This class
    /// allows you to accept command parameters in the Execute and CanExecute callback methods.
    /// </summary>
    ///
    /// <remarks>
    /// If you are using this class in WPF4.5 or above, you need to use the
    /// GalaSoft.MvvmLight.CommandWpf namespace (instead of GalaSoft.MvvmLight.Command). This will
    /// enable (or restore) the CommandManager class which handles automatic enabling/disabling of
    /// controls based on the CanExecute delegate.
    /// </remarks>
    ///
    /// <typeparam name="T">    The type of the command parameter. </typeparam>
    public class RelayCommandWpf<T> : ICommand
    {
        private readonly WeakAction<T> _execute;

        private readonly WeakFunc<T, bool> _canExecute;

        /// <summary>
        /// Initializes a new instance of the RelayCommand class that can always execute.
        /// </summary>
        ///
        /// <param name="execute">          The execution logic. IMPORTANT: If the action causes a
        ///                                 closure, you must set keepTargetAlive to true to avoid side
        ///                                 effects. </param>
        /// <param name="keepTargetAlive">  (Optional) If true, the target of the Action will be kept as
        ///                                 a hard reference, which might cause a memory leak. You should
        ///                                 only set this parameter to true if the action is causing a
        ///                                 closure. See http://galasoft.ch/s/mvvmweakaction. </param>
        ///
        /// ### <exception cref="ArgumentNullException">    If the execute argument is null. </exception>
        public RelayCommandWpf(Action<T> execute, bool keepTargetAlive = false)
            : this(execute, null, keepTargetAlive)
        {
        }

        /// <summary>   Initializes a new instance of the RelayCommand class. </summary>
        ///
        /// <exception cref="ArgumentNullException">    If the execute argument is null. </exception>
        ///
        /// <param name="execute">          The execution logic. IMPORTANT: If the action causes a
        ///                                 closure, you must set keepTargetAlive to true to avoid side
        ///                                 effects. </param>
        /// <param name="canExecute">       The execution status logic.  IMPORTANT: If the func causes a
        ///                                 closure, you must set keepTargetAlive to true to avoid side
        ///                                 effects. </param>
        /// <param name="keepTargetAlive">  (Optional) If true, the target of the Action will be kept as
        ///                                 a hard reference, which might cause a memory leak. You should
        ///                                 only set this parameter to true if the action is causing a
        ///                                 closure. See http://galasoft.ch/s/mvvmweakaction. </param>
        public RelayCommandWpf(Action<T> execute, Func<T, bool> canExecute, bool keepTargetAlive = false)
        {
            if (execute == null)
            {
                throw new ArgumentNullException("execute");
            }

            _execute = new WeakAction<T>(execute, keepTargetAlive);

            if (canExecute != null)
            {
                _canExecute = new WeakFunc<T, bool>(canExecute, keepTargetAlive);
            }
        }

        /// <summary>   Occurs when changes occur that affect whether the command should execute. </summary>
        public event EventHandler CanExecuteChanged
        {
            add
            {
                if (_canExecute != null)
                {
                    CommandManager.RequerySuggested += value;
                }
            }

            remove
            {
                if (_canExecute != null)
                {
                    CommandManager.RequerySuggested -= value;
                }
            }
        }

        /// <summary>   Raises the <see cref="CanExecuteChanged" /> event. </summary>
        [SuppressMessage(
            "Microsoft.Performance",
            "CA1822:MarkMembersAsStatic",
            Justification = "The this keyword is used in the Silverlight version")]
        [SuppressMessage(
            "Microsoft.Design",
            "CA1030:UseEventsWhereAppropriate",
            Justification = "This cannot be an event")]
        public void RaiseCanExecuteChanged()
        {
            CommandManager.InvalidateRequerySuggested();
        }

        /// <summary>
        /// Defines the method that determines whether the command can execute in its current state.
        /// </summary>
        ///
        /// <param name="parameter">    Data used by the command. If the command does not require data to
        ///                             be passed, this object can be set to a null reference. </param>
        ///
        /// <returns>   true if this command can be executed; otherwise, false. </returns>
        public bool CanExecute(object parameter)
        {
            if (_canExecute == null)
            {
                return true;
            }

            if (_canExecute.IsStatic || _canExecute.IsAlive)
            {
                if (parameter == null
                    && typeof(T).IsValueType)
                {
                    return _canExecute.Execute(default(T));
                }

                if (parameter == null || parameter is T)
                {
                    return (_canExecute.Execute((T)parameter));
                }
            }

            return false;
        }

        /// <summary>   Defines the method to be called when the command is invoked. </summary>
        ///
        /// <param name="parameter">    Data used by the command. If the command does not require data to
        ///                             be passed, this object can be set to a null reference. </param>
        public virtual void Execute(object parameter)
        {
            var val = parameter;

            if (parameter != null
                && parameter.GetType() != typeof(T))
            {
                if (parameter is IConvertible)
                {
                    val = Convert.ChangeType(parameter, typeof(T), null);
                }
            }

            if (CanExecute(val)
                && _execute != null
                && (_execute.IsStatic || _execute.IsAlive))
            {
                if (val == null)
                {
                    if (typeof(T).IsValueType)
                    {
                        _execute.Execute(default(T));
                    }
                    else
                    {
                        // ReSharper disable ExpressionIsAlwaysNull
                        _execute.Execute((T)val);
                        // ReSharper restore ExpressionIsAlwaysNull
                    }
                }
                else
                {
                    _execute.Execute((T)val);
                }
            }
        }
    }
}

有谁知道为什么

RelayCommandWpf<int>
RelayCommandWpf<double>
会导致
CanExecute
停止在WPF中工作?这些类型是否存在已知问题,或者 WPF 中的整数或双精度命令是否存在特定问题?

任何帮助或见解将不胜感激。谢谢!

c# wpf mvvm
1个回答
0
投票

XAML 中的表达式

CommandParameter="5"
将字符串分配给 CommandParameter 属性,因此下面代码片段中的
_canExecute
永远不会被调用(因为
parameter is T
为 false)。

if (parameter == null || parameter is T)
{
    return _canExecute.Execute((T)parameter);
}
© www.soinside.com 2019 - 2024. All rights reserved.