【发布时间】:2015-04-14 15:48:30
【问题描述】:
如果使用嵌套属性而不是普通属性,我很难理解为什么 ICommand.CanExecutes 总是包含以前的值而不是新值。
问题在下面描述,除了使用某种形式的“外观”模式之外,我真的无法找到解决此问题的方法,我在视图模型中创建属性并将它们挂钩到模型中的相应属性。
或者使用该死的 CommandManager.RequerySuggested 事件。这不是最佳的原因是因为视图显示了 30 多个命令,仅计算菜单,如果每次更改时所有 CanExecute 都更新,则所有菜单项/按钮都需要几秒钟才能更新。即使使用下面的示例,只有一个命令和按钮以及命令管理器,按钮也需要大约 500 毫秒来启用/禁用自身。
我能想到的唯一原因是在 CanExecute 被触发之前 CommandParameter 绑定没有更新,然后我想你对此无能为力。
提前致谢:!
例如
假设我们有这个基本的视图模型
public class BasicViewModel : INotifyPropertyChanged
{
private string name;
public string Name
{
get { return name; }
set {
this.name = value;
RaisePropertyChanged("Name");
Command.RaiseCanExecuteChanged();
}
}
private Project project;
public Project Project
{
get { return project; }
set {
if (project != null) project.PropertyChanged -= ChildPropertyChanged;
if (value != null) value.PropertyChanged += ChildPropertyChanged;
project = value;
RaisePropertyChanged("Project");
}
}
private void ChildPropertyChanged(object sender, PropertyChangedEventArgs e) {
Command.RaiseCanExecuteChanged();
}
public DelegateCommand<string> Command { get; set; }
public BasicViewModel()
{
this.Project = new Example.Project();
Command = new DelegateCommand<string>(this.Execute, this.CanExecute);
}
private bool CanExecute(string arg) {
return !string.IsNullOrWhiteSpace(arg);
}
private void Execute(string obj) { }
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName = null) {
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
还有这个模型
public class Project : INotifyPropertyChanged
{
private string text;
public string Text
{
get { return text; }
set
{
text = value;
RaisePropertyChanged("Text");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName = null)
{
var handler = this.PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
现在在我看来,我有这个文本框和按钮。
<Button Content="Button" CommandParameter="{Binding Path=Project.Text}" Command="{Binding Path=Command}" />
<TextBox Text="{Binding Path=Project.Text, UpdateSourceTrigger=PropertyChanged}" />
它有效,每次我在文本框中键入内容时都会调用 CanExecute,但参数始终设置为以前的值。假设我在文本框中写了“H”,CanExecute 被触发,参数设置为 NULL。接下来我写“E”,现在文本框包含“HE”,CanExecute 再次触发。这次仅将参数设置为“H”。
由于某种奇怪的原因,参数总是设置为以前的值,当我检查 Project.Text 时,它设置为“HE”,但参数仍设置为仅“H”。
如果我现在将命令参数更改为
CommandParameter="{Binding Path=Name}"
和 Textbox.Text 到
Text={Binding Path=Name, UpdateSourceTrigger=PropertyChanged}"
一切都很完美。 CanExecute 参数始终包含最新的值,而不是之前的值。
【问题讨论】:
-
您应该将VM的所有示例代码都放在问题中,而不是引用pastebin等外部站点
-
@AwkwardCoder 更新了问题中的所有代码,我这样做的原因是为了使问题尽可能简短,但我知道很难理解这个问题。
-
首先,您不需要在 ViewModel 中调用 Command.RaiseCanExecuteChanged(),绑定命令的重新评估应该会自动发生。
-
其次,如果您将 TextBox 绑定到 ViewModel 中的属性,则不必使用 XAML 中的 CommandParameter 将参数传回,这是多余的,我要么不传递命令参数并绑定文本框或不绑定文本框并将文本值作为命令参数传递。
-
@AwkwardCoder 嗯,如果我直接将参数绑定到textbox.Text,那么我需要为TextChanged事件创建一个事件处理程序,然后每次文本更改时调用CanExecute,感觉太多了代码在后端,但我会尝试在不走 CommandManager 路线的情况下找出解决方案。
标签: c# wpf mvvm icommand delegatecommand