Wpf 如何使用Prism 6正确制作CanExecute触发器
我有一个模型Wpf 如何使用Prism 6正确制作CanExecute触发器,wpf,mvvm,prism,delegatecommand,Wpf,Mvvm,Prism,Delegatecommand,我有一个模型 public class Irritant : BindableBase { private short _id; private string _name; private string _description; public short Id { get { return _id; } set { SetProperty(ref _id, value); } } public stri
public class Irritant : BindableBase
{
private short _id;
private string _name;
private string _description;
public short Id
{
get { return _id; }
set { SetProperty(ref _id, value); }
}
public string Name
{
get { return _name; }
set { SetProperty(ref _name, value); }
}
public string Description
{
get { return _description; }
set { SetProperty(ref _description, value); }
}
public Irritant()
{
Id = 0;
Name = "";
Description = "";
}
}
然后是两个版本的ViewModel
public class IrritantViewModel : BindableBase
{
private IrritantDb db = new IrritantDb();
//Version 1 - The Model's property is coded in IrritantViewModel
//private short _id;
//private string _name = "Alen";
//private string _description;
//public short Id
//{
// get { return _id; }
// set { SetProperty(ref _id, value); }
//}
//public string Name
//{
// get { return _name; }
// set { SetProperty(ref _name, value); }
//}
//public string Description
//{
// get { return _description; }
// set { SetProperty(ref _description, value); }
//}
//Version2 - I use the Irritant Model as property of IrritantViewModel
private DateTime? _lastUpdated;
private Irritant _entity;
public Irritant Entity
{
get { return _entity; }
set { SetProperty(ref _entity, value); }
}
public DateTime? LastUpdated
{
get { return _lastUpdated; }
set { SetProperty(ref _lastUpdated, value); }
}
public DelegateCommand UpdateCommand { get; set; }
public IrritantViewModel()
{
Entity = new Irritant();
//Version1
//UpdateCommand = new DelegateCommand(EditCommand, CanExecute).ObservesProperty(() => Name);
//Version2
UpdateCommand = new DelegateCommand(EditCommand, CanExecute).ObservesProperty(() => Entity.Name);
}
private bool CanExecute()
{
//Version1
//switch (Name)
//{
// case null:
// return false;
// case "":
// return false;
//}
//Version2
switch (Entity.Name)
{
case null:
return false;
case "":
return false;
}
return true;
}
private void EditCommand()
{
LastUpdated = DateTime.UtcNow;
}
}
这就是我的观点
public partial class IrritantView : UserControl
{
public IrritantView()
{
InitializeComponent();
DataContext = new IrritantViewModel();
}
}
<Grid >
<ScrollViewer>
<StackPanel MinWidth="200">
<TextBlock Text="Irritant" />
<!--Version 1-->
<!--<TextBlock Text="Name" />
<TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Text="Description" />
<TextBox Text="{Binding Description, UpdateSourceTrigger=PropertyChanged}" />
-->
<!--Version 2-->
<TextBlock Text="Name" />
<TextBox Text="{Binding Entity.Name, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Text="Description" />
<TextBox Text="{Binding Entity.Description, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Text="Last Updated" />
<Label Content="{Binding LastUpdated, UpdateSourceTrigger=PropertyChanged}" />
<Button Content="Save"
Command="{Binding UpdateCommand}"
/>
</StackPanel>
</ScrollViewer>
</Grid>
public部分类视图:UserControl
{
公共观点()
{
初始化组件();
DataContext=新的EntryViewModel();
}
}
版本1工作正常,当绑定到名称(TextBox Text=“{Binding Name,UpdateSourceTrigger=PropertyChanged}”)的文本框为null或为空时,“保存”按钮将禁用
但是对于版本2,保存按钮不会禁用。它只在初始化期间调用CanExecute方法,删除文本框中的文本不会禁用按钮。我做错了什么?
DelegateCommand
不会自动引发CanExecuteChanged
事件,您必须在适当的时候通过调用RaiseCanExecuteChanged
手动引发该事件。除了使用DelegateCommand
,您还可以使用RelayCommand
,它会在CommandManager.RequerySuggested
事件上进行中继,该事件会为您执行类似的操作
ICommand
:
public ICommand UpdateCommand { get; set; }
UpdateCommand = new AutoCanExecuteCommand(new DelegateCommand(EditCommand, CanExecute));
public class AutoCanExecuteCommand : ICommand
{
public ICommand WrappedCommand { get; private set; }
public AutoCanExecuteCommand(ICommand wrappedCommand)
{
if (wrappedCommand == null)
{
throw new ArgumentNullException("wrappedCommand");
}
WrappedCommand = wrappedCommand;
}
public void Execute(object parameter)
{
WrappedCommand.Execute(parameter);
}
public bool CanExecute(object parameter)
{
return WrappedCommand.CanExecute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
}
DelegateCommand
不会自动引发CanExecuteChanged
事件,您必须在适当时通过调用RaiseCanExecuteChanged
手动引发该事件。除了使用DelegateCommand
,您还可以使用RelayCommand
,它会在CommandManager.RequerySuggested
事件上进行中继,该事件会为您执行类似的操作
ICommand
:
public ICommand UpdateCommand { get; set; }
UpdateCommand = new AutoCanExecuteCommand(new DelegateCommand(EditCommand, CanExecute));
public class AutoCanExecuteCommand : ICommand
{
public ICommand WrappedCommand { get; private set; }
public AutoCanExecuteCommand(ICommand wrappedCommand)
{
if (wrappedCommand == null)
{
throw new ArgumentNullException("wrappedCommand");
}
WrappedCommand = wrappedCommand;
}
public void Execute(object parameter)
{
WrappedCommand.Execute(parameter);
}
public bool CanExecute(object parameter)
{
return WrappedCommand.CanExecute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
}
出于一些原因,我不建议与CommandManager挂钩。除了内存泄漏,它还可能在应用程序中引入性能问题,因为您无法控制CommandManager何时或多少次调用CanExecute(发生在UI线程上)。相反,我建议您使用模型对象的INPC,如下所示:
我不建议挂接CommandManager,原因有很多。除了内存泄漏,它还可能在应用程序中引入性能问题,因为您无法控制CommandManager何时或多少次调用CanExecute(发生在UI线程上)。相反,我建议您使用模型对象的INPC,如下所示:
DelegateCommand.ObserveSProperty不支持复杂的对象属性。它仅支持定义命令时ViewModel上存在的属性。这是因为复杂对象的生命周期是未知的,如果创建了该对象的多个实例,就会产生内存泄漏。所以建议使用版本1如果我使用版本1,那么它将只是VVM而不是MVVM?DelegateCommand.ObserveSProperty不支持复杂的对象属性。它仅支持定义命令时ViewModel上存在的属性。这是因为复杂对象的生命周期是未知的,如果创建了该对象的多个实例,就会产生内存泄漏。所以建议使用版本1如果我使用版本1,那么它将只是VVM而不是MVVM?