什么是之间的差异的RoutedCommand和RelayCommand?何时使用RoutedCommand以及何时在MVVM模式中使用RelayCommand?
我在使用GalaSoft MVVM Light框架将参数传递给relaycommand时遇到问题.我知道mvvm light的relay命令的实现不使用lambda参数,所以我做了一些研究,并找到了一种方法,人们通过做这样的事情来解决它:
public RelayCommand ProjMenuItem_Edit
{
get
{
if (_projmenuItem_Edit == null)
{
//This should work....
_projmenuItem_Edit = new RelayCommand(ProjEditNode);
}
return _projmenuItem_Edit;
}
}
private void ProjEditNode(object newText)
{
var str = newText as string;
OrganLocationViewModel sel =
ProjectOrganLocationView.GetExtendedTreeView().GetTopNode();
//Console.WriteLine(sel.OrganDisplayName);
sel.OrganDisplayName = str;
}
Run Code Online (Sandbox Code Playgroud)
但是,我一直在_projmenuItem_Edit = new RelayCommand(ProjEditNode);说错误Argument 1: cannot convert from 'method group' to 'System.Action'
我错过了什么?
我最近在WPF中编程很多,但我的View和ViewModel在这一点上并不是分开的.嗯,这是部分原因.我的所有绑定都与文本框中的文本,标签内容,数据网格中的列表,......相关,都是由常规属性完成的,其中包含NotifyPropertyChanged事件.
处理按钮点击或文本更改内容的所有事件都是通过链接事件来完成的.现在,我想开始使用命令并找到这篇文章:http://www.codeproject.com/Articles/126249/MVVM-Pattern-in-WPF-A-Simple-Tutorial-for-Absolute.它解释了如何设置MVVM,但我对它感到困惑RelayCommand.
它做了什么工作?它对我表单中的所有命令都可用吗?如果(a)某些文本框未填写,如何禁用该按钮?
编辑1:
对"我的表单中的所有命令都可用吗?"的一个很好的解释.在这里回答:https://stackoverflow.com/a/22286816/3357699
这是我到目前为止的代码:https://stackoverflow.com/a/22289358/3357699
考虑参考Josh Smith的文章WPF Apps with Model-View-ViewModel Design Pattern,特别是a的示例实现RelayCommand(图3).(无需阅读整篇文章以了解此问题.)
在一般情况下,我想实现是优秀的,但我有一个代表团的问题CanExecuteChanged订阅到CommandManager的RequerySuggested事件.各州的文件RequerySuggested:
由于此事件是静态的,因此它只会作为弱引用保留在处理程序中.侦听此事件的对象应该对其事件处理程序保持强引用,以避免它被垃圾回收.这可以通过拥有私有字段并在附加到此事件之前或之后将处理程序指定为值来实现.
然而,示例实现RelayCommand不会对订阅的处理程序维护任何此类:
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
Run Code Online (Sandbox Code Playgroud)
RelayCommand客户端的漏洞,要求用户RelayCommand了解自己的实施CanExecuteChanged并维护一个实时参考?如果是这样,例如,修改实现RelayCommand类似于以下内容以减轻CanExecuteChanged订户的潜在过早GC 是否有意义:
// This event never actually fires. It's purely lifetime mgm't.
private event EventHandler canExecChangedRef;
public event EventHandler CanExecuteChanged
{
add
{
CommandManager.RequerySuggested += value; …Run Code Online (Sandbox Code Playgroud)所以我的第一次尝试完成了后面的代码,现在我正在尝试重构我的代码以使用MVVM模式,遵循MVVM 在框信息中的指导.
我已经创建了一个viewmodel类来匹配我的视图类,并且我将代码从代码中移出到viewmodel中,从命令开始.
我的第一个障碍是尝试实现一个"关闭"按钮,如果数据未被修改则关闭窗口.我已经装配了一个CloseCommand来替换'onClick'方法,除了代码试图运行的地方外,一切都很好this.Close().显然,由于代码已从窗口移动到普通类,因此'this'不是窗口,因此不可关闭.但是,根据MVVM,viewmodel不知道视图,所以我无法调用view.Close().
有人可以建议我如何从viewmodel命令关闭窗口?
我从Josh Smith的MVVM 教程中获得了以下代码.
任何人都可以快速解释这段代码实际上做了什么吗?
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
Run Code Online (Sandbox Code Playgroud)
我无法理解两件事:
CanExecuteChanged事件呢?CommandManager.RequerySuggested做什么的?上面的代码RelayCommand来自这里的Class .
如果您正在使用MVVM并使用命令,您通常会在ViewModel上看到由私有RelayCommand或DelegateCommand字段支持的ICommand属性,例如MSDN上原始MVVM文章中的此示例:
RelayCommand _saveCommand;
public ICommand SaveCommand
{
get
{
if (_saveCommand == null)
{
_saveCommand = new RelayCommand(param => this.Save(),
param => this.CanSave );
}
return _saveCommand;
}
}
Run Code Online (Sandbox Code Playgroud)
然而,这很麻烦,并且使得设置新命令相当繁琐(我与一些资深的WinForms开发人员合作,他们对所有这些打字都不屑一顾).所以我想简化它并挖掘一下.我在get {}块的第一行设置了一个断点,看到它只是在我的应用程序第一次加载时才被击中 - 我可以随后发出尽可能多的命令,这个断点永远不会被击中 - 所以我想要简化这个以从我的ViewModel中删除一些混乱,并注意到以下代码的工作方式相同:
public ICommand SaveCommand
{
get
{
return new RelayCommand(param => this.Save(), param => this.CanSave );
}
}
Run Code Online (Sandbox Code Playgroud)
但是,我不太了解C#或垃圾收集器,知道这是否会导致问题,例如在某些情况下产生过多的垃圾.这会造成任何问题吗?
我在viewmodel中定义了以下代码.我认为类型的SaveAsync Func<Task>正在转换为Action,因为RelayCommand采取的Action不是a,Func<Task>但我不清楚它的含义.
1)RelayCommand是否需要用异步版本(RelayCommandAsync)替换?2)当前代码在异步方面究竟做了什么?3)如果有什么可以/我应该改变以改善/纠正它怎么办?
private ICommand _saveCommand;
public ICommand SaveCommand
{
get { return _saveCommand ?? (_saveCommand = new RelayCommand(async () => await SaveAsync(), CanSave)); }
}
public bool CanSave()
{
return !IsBusy;
}
private async Task SaveAsync()
{
IsBusy = true;
try
{
await _service.SaveAsync(SomeProperty);
}
catch ( ServiceException ex )
{
Message = "Oops. " + ex.ToString();
}
finally
{
IsBusy = false;
}
}
Run Code Online (Sandbox Code Playgroud)
谢谢!
编辑:经过一些实验后,似乎异步方法本身就能正常工作.是否包含在lambda中的async/await以及该方法是否定义为async Task或没有区别async void.
但是,无法正常工作的是canExecute …
我正在使用MVVM Light V3 alpha 3编写一个WPF 4应用程序(使用VS2010 RC),并且在这里遇到了一些奇怪的行为......
我有一个打开a的命令,Window那个Window创建了ViewModel等等 - 那里没什么奇怪的.
在那Window我有一些RelayCommands,例如:
CategoryBeenSelected = new RelayCommand(() => OnCategoryUpdate = true);
Run Code Online (Sandbox Code Playgroud)
没有什么奇怪的 - 它按照我的预期工作.
问题是我不能使用通用RelayCommand的CanExecute方法/ lambda表达式.
这有效:
DeleteCategoryCommand = new RelayCommand<int>(DeleteCategory);
Run Code Online (Sandbox Code Playgroud)
但这不是:
DeleteCategoryCommand = new RelayCommand<int>(DeleteCategory, CanDeleteCategory);
Run Code Online (Sandbox Code Playgroud)
窗口没有出现.我的意思是,我单击打开窗口的按钮,应用程序刚刚被阻止,几秒钟后,Window的InitializeComponent方法抛出一个NullReferenceException(对象引用未设置为对象的实例)
简而言之,如果我将一个CanExecute方法放在a上RelayCommand<T>,Window那么拥有 ViewModel(带有)的那个RelayCommand<T>就无法实例化.如果我删除了CanExecute,Window显示出来.
这里的问题在哪里?我糊涂了.
谢谢.
编辑:根据要求,这是堆栈跟踪:
A first chance exception of type 'System.NullReferenceException' occurred in PresentationFramework.dll at GalaSoft.MvvmLight.Command.RelayCommand`1.CanExecute(Object parameter) at System.Windows.Controls.Primitives.ButtonBase.UpdateCanExecute() at …
relaycommand ×10
wpf ×8
c# ×7
mvvm ×6
icommand ×2
.net ×1
async-await ×1
asynchronous ×1
binding ×1
command ×1
mvvm-light ×1