【问题标题】:WPF MVVM - Simple Binding Button with Textbox (Icommand)WPF MVVM - 带文本框的简单绑定按钮(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; }
    }

【问题讨论】:

    标签: c# wpf mvvm binding icommand


    【解决方案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 的更改立即反映在文本框中,这是必要的。
    猜你喜欢
    • 1970-01-01
    • 2013-08-05
    • 2015-10-16
    • 2011-08-28
    • 2012-11-24
    • 2021-04-02
    • 2017-07-30
    • 2014-06-19
    • 1970-01-01
    相关资源
    最近更新 更多