我目前正在处理 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 中的整数或双精度命令是否存在特定问题?
任何帮助或见解将不胜感激。谢谢!
XAML 中的表达式
CommandParameter="5"
将字符串分配给 CommandParameter 属性,因此下面代码片段中的 _canExecute
永远不会被调用(因为 parameter is T
为 false)。
if (parameter == null || parameter is T)
{
return _canExecute.Execute((T)parameter);
}