WPF MVVM - 带文本框的简单绑定按钮(Icommand)

Posted

技术标签:

【中文标题】WPF MVVM - 带文本框的简单绑定按钮(Icommand)【英文标题】:WPF MVVM - Simple Binding Button with Textbox (Icommand) 【发布时间】:2019-03-04 14:42:40 【问题描述】:

我要做的是:当文本框包含值“123”时,它应该启用按钮并允许我单击它。

Solution Image & View Image

我找不到根据我的 Button 参数触发 Button 命令(名为 SpecialCommand.cs 的类)的方法。你能支持我在哪里弄错了这个 MVVM 模式吗?

WPF 视图 [MainWindow.xaml]:

<Window.Resources>
    <ViewModel:MainWindowVM x:Key="WindowVm"></ViewModel:MainWindowVM>
</Window.Resources>

<Grid>
    <StackPanel>
        <TextBox x:Name="textBox" Margin="0, 5" Text="123"/>
        <Button Content="Click me!" Margin="0, 5" Command="Binding SpecialCommand, Source=StaticResource WindowVm" CommandParameter="Binding Text, ElementName=textBox, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay"/>
    </StackPanel>
</Grid>

ViewModel [MainWindowVM.cs]:

public class MainWindowVM

    private SpecialCommand _specialCommand;

    public SpecialCommand SpecialCommand  get => _specialCommand; set => _specialCommand = value; 

    public MainWindowVM()
    
        _specialCommand = new SpecialCommand();
    


命令 [SpecialCommand.cs]

public class SpecialCommand : ICommand

    public bool CanExecute(object parameter)
    
        if (parameter != null && (parameter as string) == "123")
            return true;

        return false;
    

    public void Execute(object parameter)
    
        MessageBox.Show("Button Pressed!");
    

    public event EventHandler CanExecuteChanged;

我相信,也许这就是我弄错了,因为按钮和文本框在视图中,我不需要在我的 SpecialCommand 实现中添加/修改任何方法。他们应该能够看到属性何时更改。 就像下面的 CanExecuteChanged() 一样,这个命令会引发很多次,对于这个小任务来说似乎有点过分了。

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

【问题讨论】:

【参考方案1】:

没有必要让它变得比需要的更复杂。

public class MainWindowVM

    private string m_OneTwoThree;
    public string OneTwoThree
        get  return OneTwoThree; 
        set 
            if (m_OneTwoThree != value)
                m_OneTwoThree = value;
                NotifyPropertyChanged(nameof(OneTwoThree)); //if you need this let me know
            
        
           

    public MainWindowVM()
    

    


    public ICommand RandomCommand  get  return new RelayCommand(OnRandom, IsOneTwoThree);  


    private void OnRandom()
    
        //do stuff
    

    private bool IsOneTwoThree()
        if (OneTwoThree == "123")
            return true;
        
        return false;
    

您还必须更新您的xaml 我认为它无法找到“OneTwoThree”,因此您必须自己绑定它,但您通常会这样做。

<StackPanel>
    <TextBox x:Name="textBox" Margin="0, 5" Text="Binding OneTwoThree"/>
    <Button Content="Click me!" Margin="0, 5" Command="Binding RandomCommand, Source=StaticResource WindowVm"/>
</StackPanel>

如果您有任何问题,请随时提出。

这是我的 RelayCommand :使用 RelayCommand("要执行的东西", "如果函数返回 true 你可以执行");

public class RelayCommand : ICommand

    public event EventHandler CanExecuteChanged
    
        add  CommandManager.RequerySuggested += value; 
        remove  CommandManager.RequerySuggested -= value; 
    
    private Action methodToExecute;
    private Func<bool> canExecuteEvaluator;
    public RelayCommand(Action methodToExecute, Func<bool> canExecuteEvaluator)
    
        this.methodToExecute = methodToExecute;
        this.canExecuteEvaluator = canExecuteEvaluator;
    
    public RelayCommand(Action methodToExecute)
        : this(methodToExecute, null)
    
    
    public bool CanExecute(object parameter)
    
        if (this.canExecuteEvaluator == null) 
            return true;
        
        else 
            bool result = this.canExecuteEvaluator.Invoke();
            return result;
        
    
    public void Execute(object parameter)
    
        this.methodToExecute.Invoke();
    

【讨论】:

嗨,马特,感谢您的回复。我喜欢你的 RelayCommand 实现。是的,它按预期工作,没有任何奇怪的行为,如以前的答案。我唯一认为这会在我的代码中创建另外两个额外的方法,但如果这是最好的方法,我将从现在开始使用它。 我唯一不明白的是为什么我们有 CanExecute: 对象参数。看起来我们不需要这个参数,也没有办法将它与 Button CommandParameter = "" 一起实际使用。 CanExecute 检查IsOneTwoThree() 是否返回 true 或 false,如果为 true,则启用按钮,如果为 false,则禁用按钮。 您不需要使用CommandParameter,因为TextBox 已绑定到ViewModel 上的OneTwoThree 字段,我们只需检查OneTwoThree == "123" 上的字段是否为OneTwoThree == "123" 还用NotifyPropertyChanged更新了代码【参考方案2】:

你需要一种方法来告诉命令它应该检查它是否CanExecute。这是通过触发事件CanExecuteChanged 来完成的。它告诉按钮重新查询CanExecute 属性。

为此,我将向您的视图模型添加一个文本属性并将文本框绑定到它。

SpecialCommand中添加方法:

public void TriggerCanExecuteChanged()

    CanExecuteChanged?.Invoke(this, EventArgs.Empty);

MainWindowVM类中添加属性:

private string _text;
public string Text

    get  return _text; 
    set 
        if (value != _text) 
            _text = value;
            _specialCommand.TriggerCanExecuteChanged();
        
    


查看模型以实现INotifyPropertyChanged(参见 cmets):

public class MainWindowVM : INotifyPropertyChanged

    public SpecialCommand SpecialCommand  get; set;  = new SpecialCommand();

    private string _text;
    public string Text
    
        get  return _text; 
        set 
            if (value != _text) 
                _text = value;
                OnPropertyChanged(nameof(Text));
                SpecialCommand.TriggerCanExecuteChanged();
            
        
    

    public event PropertyChangedEventHandler PropertyChanged;

    private void OnPropertyChanged(string propertyName)
    
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    

【讨论】:

嗨奥利弗,感谢您的回复。我已经尝试过您的代码,但我遇到了问题。看起来如果我引发/触发 TriggerCanExecuteChanged() 方法,则 SpecialCommand 正在接收的值与属性本身之间存在延迟。我这里有什么,我需要输入“1234”,这样它就会升起并启用我的按钮。 我做了另一个测试,看起来我的 Button 在属性值更改之前接收到旧参数。也许这张图片会更好地解释问题是什么:imgur.com/3eNldov。新值为 1234,旧值为 _text = "123"。所以参数是一个延迟的文本属性结果。很奇怪? 不使用此参数,您可以将 VM 的引用传递给命令并使用 Text 属性。在文本框中使用UpdateSourceTrigger=PropertyChanged。如果你不这样做,按钮可能只会在验证时更新,即在离开 thextbox 之后。您还可以让 VM 实现 INotifyPropertyChangedText 属性在更改时触发 PropertyChanged 事件。如果您希望对 VM 的更改立即反映在文本框中,这是必要的。

以上是关于WPF MVVM - 带文本框的简单绑定按钮(Icommand)的主要内容,如果未能解决你的问题,请参考以下文章

按钮上的绑定命令不起作用wpf mvvm

wpf中mvvm的Command绑定后,如何在点击按钮的时候在viewmodel里面异步执行方法。

C#:WPF MVVM 中的按钮绑定

WPF中MVVM子窗口修改数据问题

WPF 绑定密码

WPF MVVM 文本框文本绑定与 changedText 事件