WPF如何更新CanExecute

时间:2014-08-13 15:07:32

标签: c# wpf xaml mvvm

我有以下问题。

我有以下简单的xaml:

<TextBox Name="NameBox" Text ="{Binding Name}" />
<Button Content="Save" Command="{Binding SaveCommand}" CommandParameter="{Binding Entity}"  />

我将此Window的DataContext绑定到以下View Model

public class MyViewModel
{
    public SimpleModel Entity { get; set; }

    private ICommand _saveCommand;

    public ICommand SaveCommand { get { return _saveCommand ?? (_saveCommand = new MyCommand(OnSaveItem, parameter => CanSaveItem())); } }

    public void OnSaveItem(object parameter) 
    {
        // some code
    }

    public virtual bool CanSaveItem()
    {
        return !String.IsNullOrWhiteSpace(Entity.Name);
    }

}      

SimpleModel是

public class SimpleModel
{        
    public int Id { get; set; }

    public string Name { get; set; }       
}

这段代码大部分都是正确的,但我不能 make方法CanSaveItem正常工作。我不知道如何告诉SaveCommand ViewModel的属性已更改。我知道我必须使用CanExecuteChanged或CommandManager.InvalidateRequerySuggested,我试图使用他们的一些但我不知道如何正确地做它并没有生效。你能帮我解决这个问题吗?

UPD。

public class MyCommand : ICommand
{        
    public MyCommand(Action<object> execute, Predicate<object> canExecute)
    {
        _canExecute = canExecute;
        _execute = execute;
    }

    public bool CanExecute(object parameter)
    {
        return _canExecute(parameter);
    }

    public void Execute(object parameter)
    {                       
        _execute(parameter);
    }

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    private readonly Predicate<object> _canExecute;

    private readonly Action<object> _execute;
}

2 个答案:

答案 0 :(得分:3)

看起来你处于早期的学习曲线上,这可能令人困惑......有时候对我来说也是如此。

无论如何,我对你所拥有的东西做了一些细微的改动,并解释了我对他们的所作所为。

public class MyViewModel
{
    public SimpleModel Entity { get; set; }

    private MyCommand _saveCommand;

    public MyCommand SaveCommand { get { return _saveCommand ?? (_saveCommand = new MyCommand(OnSaveItem, parameter => CanSaveItem())); } }

    public MyViewModel()
    {
        //------ You need to create an instance of your entity to bind to
        Entity = new SimpleModel();

        //-- I added an event handler as your "Entity" object doesn't know 
        //-- about the button on the view model.  So when it has something
        //-- change, have it call anybody listening to its exposed event.
        Entity.SomethingChanged += MyMVVM_SomethingChanged;
    }

    void MyMVVM_SomethingChanged(object sender, EventArgs e)
    {
        // Tell our mvvm command object to re-check its CanExecute
        SaveCommand.RaiseCanExecuteChanged();
    }

    public void OnSaveItem(object parameter)
    {
        // some code
    }

    public virtual bool CanSaveItem()
    {
        //-- Checking directly to your Entity object
        return !String.IsNullOrWhiteSpace(Entity.Name);
    }
}


public class SimpleModel
{
    //-- Simple constructor to default some values so when you run
    //-- your form, you SHOULD see the values immediately to KNOW
    //-- the bindings are correctly talking to this entity. 
    public SimpleModel()
    {
        _name = "test1";
        _Id = 123;
    }

    //-- changed to public and private... and notice in the setter
    //-- to call this class's "somethingChanged" method
    private int _Id;
    public int Id
    {
        get { return _Id; }
        set
        {
            _Id = value;
            somethingChanged("Id");
        }
    }

    private string _name;
    public string Name 
    { get { return _name; }
        set { _name = value;
                somethingChanged( "Name" );
        }
    }

    //-- Expose publicly for anything else to listen to (i.e. your view model)
    public event EventHandler SomethingChanged;

    //-- So, when any property above changes, it calls this method with whatever
    //-- its property is just as a reference.  Then checks.  Is there anything
    //-- listening to our exposed event handler?  If so, pass the information on
    private void somethingChanged( string whatProperty)
    {
        // if something is listening
        if (SomethingChanged != null)
            SomethingChanged(whatProperty, null);
    }


}


public class MyCommand : ICommand
{
    public MyCommand(Action<object> execute, Predicate<object> canExecute)
    {
        _canExecute = canExecute;
        _execute = execute;
    }

    public bool CanExecute(object parameter)
    {
        return _canExecute(parameter);
    }

    public void Execute(object parameter)
    {
        _execute(parameter);
    }

    private readonly Predicate<object> _canExecute;

    private readonly Action<object> _execute;

    //-- Change to the event handler definition, just expose it
    public event EventHandler CanExecuteChanged;

    //-- Now expose this method so your mvvm can call it and it rechecks 
    //-- it's own CanExecute reference
    public void RaiseCanExecuteChanged()
    {
        if (CanExecuteChanged != null)
            CanExecuteChanged(this, new EventArgs());
    }
}

最后,表单中的绑定。我不知道你是如何将视图的“DataContext”设置为视图模型的,但假设这一切都正确且没有问题,请将文本框和命令按钮调整为类似

<TextBox Name="NameBox" Text ="{Binding Entity.Name, 
    NotifyOnTargetUpdated=True, UpdateSourceTrigger=PropertyChanged}"  />

<Button Content="Save" Command="{Binding SaveCommand}" CommandParameter="{Binding Entity}"  />

请注意,文本绑定是MVVM上的“Entity”对象,然后是Entity对象的“.Name”属性。这里重要的是UpdateSourceTrigger。这会强制更新每个字符更改的数据绑定,因此只要删除最后一个字符,或者开始键入第一个字符,就会分别刷新“保存”按钮。

答案 1 :(得分:1)