在查看 Josh Smith 关于 的文章时CommandGroup,我注意到互联网上有许多关于如何实施ICommand.CanExecuteChanged.
在 StackOverflow 上发布了一个类似的问题,但是
以供参考:
CommandGroupRelayCommand使用CommandManager 实现的CanExecuteChangedRoutedCommand使用CommandManager 实现的CanExecuteChanged我对 WPF 比较陌生,我想知道CanExecuteChanged应该如何在 Josh Smith's 中实现该事件CommandGroup以避免任何意外行为或内存泄漏?
Josh Smith:使用 CommandGroup 聚合 WPF 命令
Josh Smith:采用 MVVM 设计模式的 WPF 应用程序
StackOverflow:Josh Smith 的 RelayCommand 实现有缺陷吗?
我的 WPF 项目中有一个按钮,当我按住按钮时,我希望它一遍又一遍地执行相同的命令。我可以使用 RepeatButton,但我的偏好是命令一旦运行完成(在它自己的任务中)就立即再次执行,而不是依赖于 RepeatButton 控件的延迟和间隔属性。
我不介意创建一个按钮单击方法,但命令操作长时间运行,执行时间将取决于 ExecuteParameter 的值(在这种情况下,代表机器物理位置的双精度元组)。
XAML:
<Button FontFamily="Marlett" FontSize="40" Content="5" HorizontalAlignment="Center" VerticalAlignment="Top" Margin="100" Width="100" Height="50"
Command="{Binding IncrBAnglePos}"
CommandParameter="{Binding ElementName=slider, Path=Value}">
</Button>
Run Code Online (Sandbox Code Playgroud)
C#:
SystemCommands.AddSubSystemCommand(SystemRef, CommandNames.IncrAngle, new RelayCommand(
o =>
{
double AngleIncr = (double)o > 5 ? 5 : (double)o;
double nextX = MotionControl.LiveX;
double nextB = MotionControl.LiveB + AngleIncr;
nextB = nextB >= 45 ? 45 : nextB;
Task.Run(() =>
{
SystemCommands.ExecuteCommand(CommandNames.GotoPosition, new Tuple<double,double>(nextX, nextB));
});
},
_ =>
{
if (MotionControl == null)
return false; …Run Code Online (Sandbox Code Playgroud) 我有一个非常简单的应用程序与a TextBox和a Button.当输入的文本TextBox长度超过5个字符时,将启用该按钮.这是我的ViewModel的代码:
private string _text { get; set; }
public string Text
{
get { return _text; }
set
{
_text = value;
OnPropertyChanged("Text");
}
}
private ICommand _buttonCommand;
public ICommand ButtonCommand
{
get
{
if (_buttonCommand == null)
{
_buttonCommand = new RelayCommand(
param => this.ButtonCommandExecute(),
param => this.ButtonCommandCanExecute()
);
}
return _buttonCommand;
}
}
private bool ButtonCommandCanExecute()
{
if (this.Text.Length < 5)
{
return false;
}
else
{
return true;
}
}
private …Run Code Online (Sandbox Code Playgroud) 我不知道Josh Smith和Laurent Bugnion的RelayCommand的实现是否有区别,但在我看过的每一个地方,听起来像RelayCommand的Execute部分可以带0或1个参数.我只能使它与0一起工作.当我尝试类似的东西时:
public class Test
{
public RelayCommand MyCommand { get; set; }
public Test()
{
MyCommand = new RelayCommand((param) => SomeFunc(param));
}
private void SomeFunc( object param)
{
}
}
Run Code Online (Sandbox Code Playgroud)
我收到错误:Delegate 'System.Action' does not take '1' arguments.为了确保我不是疯了,我去了RelayCommand的定义,以确保我的解决方案中没有一些流氓实现,但果然,它只是Action,而不是Action <>.
我到底在这里错过了什么?
我正在阅读这篇关于MVVM的
MSDN文章.我现在正在看图#15中的RelayCommand.假装我想测试这个SaveCommand.我该怎么办?我正在使用NUnit和Rhino Mocks 3.6
我使用绑定到使用CanExecute委托初始化的RelayCommands的几个按钮.
RelayCommand DeleteCommand;
bool CanDelete()
{
return BoolProp1 && BoolProp2;
}
...
DeleteCommand = new RelayCommand(Delete, CanDelete);
Run Code Online (Sandbox Code Playgroud)
BoolProp1并且BoolProp2是正确的属性,setter正确提升PropertyChanged,但众所周知,这还不足以使SL重新评估CanExecute命令.这就是为什么我也打电话给Delete.RaiseCanExecuteChanged()两个二传手.
所有这些工作正常(按钮被禁用并正确启用)到某一点,所有停止.在这一点上,呼叫Delete.RaiseCanExecuteChanged()不再触发我的断点CanDelete(),按钮永远保持原样.
我花了2个小时试图找出确切原因而没有效果.我怀疑RaiseCanExecuteChanged()在单个"绑定迭代"期间多次调用会以某种方式破坏机制.
任何提示?我已经在考虑使用额外的IsExecutable字段刷新INotifyPropertyChanged...
UPDATE
RelayCommand实际上是GalaSoft.MvvmLight.Command.RelayCommand来自MVVM Light Toolkit.ILSpy展示了一个非常简单的ICommand实现:
public bool CanExecute(object parameter)
{
return this._canExecute == null || this._canExecute.Invoke();
}
public void RaiseCanExecuteChanged()
{
EventHandler canExecuteChanged = this.CanExecuteChanged;
if (canExecuteChanged != null)
{
canExecuteChanged.Invoke(this, EventArgs.Empty);
}
}
Run Code Online (Sandbox Code Playgroud)
有_canExecute …
这没有做任何事情,但导致需要不必要的投射(或者更确切地说,导致我下拉代码库并自己进行更改).这样做是否有理由?
参考文献:
编辑 这是一个例子:
DoCommand = new RelayCommand<AsyncCallback>((callBack) =>
{
Console.WriteLine("In the Action<AsyncCallback>");
SomeAsyncFunction((async_result) =>
{
Console.WriteLine("In the AsyncCallback");
callBack.Invoke(new MyAsyncResult(true));
});
});
DoCommand.Execute((iasyncresult) => Console.WriteLine(iasyncresult.IsCompleted));
//Where MyAsyncResult is a class implement IAsyncResult that sets IsCompleted in the constructor
// This will cause the "cannot cast lambda as object" error
Run Code Online (Sandbox Code Playgroud) 我正在使用RelayCommand处理按钮单击,我需要获取sender参数,但它始终为null,任何想法为什么?
ViewModel.cs
private RelayCommand _expandClickCommand;
public ICommand ExpandClickCommand
{
get
{
if (_expandClickCommand == null)
{
_expandClickCommand = new RelayCommand(ExpandClickCommandExecute, ExpandClickCommandCanExecute);
}
return _expandClickCommand;
}
}
public void ExpandClickCommandExecute(object sender)
{
//sender is always null when i get here!
}
public bool ExpandClickCommandCanExecute(object sender)
{
return true;
}
Run Code Online (Sandbox Code Playgroud)
View.xaml
<ListBox ItemsSource="{Binding Path=MyList}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Button Grid.Column="0" Grid.Row="0" Content="Expand" Command="{Binding DataContext.ExpandClickCommand,ElementName=SprintBacklog}"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Run Code Online (Sandbox Code Playgroud)
我需要在ExpandClickCommand中获取当前ListboxItem的索引
当我PressCommand.RaiseCanExecuteChanged();在TimerOnElapsed方法中调用时,没有任何反应.
可能是什么问题呢?(GalaSoft.MvvmLight.WPF4 v4.0.30319和GalaSoft.MvvmLight.Extras.WPF4 v4.0.30319)
这是我的测试代码:
using System.Timers;
using System.Windows;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
namespace CommandTest {
public class MainWindowVM : ViewModelBase {
public MainWindowVM() {
PressCommand = new RelayCommand(
() => MessageBox.Show("Pressed"),
() => _canExecute);
PressCommand.CanExecuteChanged += (sender, args) => System.Diagnostics.Debug.WriteLine(System.DateTime.Now.ToLongTimeString() + " CanExecuteChanged");
_timer = new Timer(1000);
_timer.Elapsed += TimerOnElapsed;
_timer.Enabled = true;
}
public RelayCommand PressCommand { get; private set; }
#region Private
private void TimerOnElapsed(object sender, ElapsedEventArgs elapsedEventArgs) {
_canExecute = !_canExecute;
PressCommand.RaiseCanExecuteChanged(); …Run Code Online (Sandbox Code Playgroud) 我正在研究中继命令的完整实现,可以在这里找到
我听说 RelayCommand 背后的想法是拥有一种“通用遥控器”来使用您的所有命令。
如果是这样的话,我在实施方面有两个问题:
1)如果对于某些控件我不想传递参数会发生什么?我一定要吗?我是否需要相应地更改我的执行/可以执行函数以支持这些参数?
2) 如果我不想在 XAML 中传递 CommandParameter 怎么办?如果我想通过使用属性更改或代码中的其他方法来影响控件的更改。我可以在不传递 XAML 中的 CommandParameter 的情况下实现 CanExecute 或 CanExecuteChanged 吗?
到目前为止,我主要实现了部分 RelayCommands,其中 CanExecute 始终返回 true,并且我只是将控件 IsEnabled 绑定到视图模型中的额外属性。这工作得很好,但我想知道 - 完整的实施能为我做什么?
(答案能否给出完整的工作示例?)