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?